Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 1 de marzo de 2016
ASP.NET CoreASP.NET Core MVC introduce algunas características interesantes que no existían ni tenían un equivalente directo en ASP.NET MVC 5 y anteriores, y entre ellas hay una que me ha llamado mucho la atención y que vamos a comentar en este post: las convenciones personalizadas.

Seguro recordaréis que tradicionalmente tanto MVC como Web API han sido frameworks muy guiados por convenciones, es decir, venían de serie diseñados con un conjunto de pautas o normas a las que debíamos ceñirnos si queríamos aprovechar todo su potencial.

Teníamos convenciones para las rutas, de ubicación de vistas, para nombrar los controladores, nombrado de acciones según el verbo HTTP mediante el cual se pretendía acceder a una función del API, y algunas más.

Algunas de estas convenciones se podían cambiar fácilmente usando mecanismos directos proporcionados por los propios frameworks, pero había otras que era bastante más difícil hacerlo y a veces teníamos que hacer auténticos malabarismos con las piezas internas del marco de trabajo para conseguir nuestros objetivos.

ASP.NET Core MVC simplifica bastante la adaptación del framework a nuestras necesidades, proporcionando fórmulas muy potentes para la creación de convenciones personalizadas.

<Disclaimer>En el momento de escribir estas líneas ASP.NET MVC Core 1.0 se encuentra en RC1, por lo que algunos detalles aún pueden variar en la versión final</Disclaimer>


Todo empieza durante el arranque de una aplicación ASP.NET Core MVC, momento en que el framework analiza los ensamblados y crea en memoria un modelo de aplicación, representado por una instancia de clase ApplicationModel.

En su interior encontramos información muy detallada sobre los filtros definidos, controladores, acciones, rutas, etc. La siguiente captura de pantalla muestra el contenido del ApplicationModel de la aplicación MVC Core creada por defecto usando las plantillas de Visual Studio:

Interior de ApplicationModel
Y aunque esto ya es bastante interesante de por si, lo que viene ahora es aún mejor: durante el arranque tenemos acceso al ApplicationModel y podemos "retocarlo" a nuestro antojo para crear nuestras propias convenciones.

¿Y qué tipo de cosas podríamos conseguir con este mecanismo? Pues todas las que nos permita nuestra imaginación: añadir o quitar filtros a controladores en función del criterio que queramos, cambiar nombres de controladores o acciones sobre la marcha, modificar propiedades como los verbos HTTP permitidos, o incluso añadir o eliminar acciones o controladores sobre la marcha (!)

Creación de convenciones personalizadas

Las convenciones personalizadas son clases que implementan el interfaz IApplicationModelConvention, cuyo contrato es el siguiente:
public interface IApplicationModelConvention
{
    void Apply(ApplicationModel application);
}
Durante el arranque, una vez el framework ha terminado de poblar el ApplicationModel, invocará a los métodos Apply() de todas las convenciones que hayan sido registradas, y es ahí donde manipularemos el modelo de aplicación, que nos llegará como parámetro, para adaptarlo a nuestros intereses.

El registro de convenciones se realiza desde el método ConfigureServices() de la clase Startup, justo en el momento de añadir MVC al sistema de inyección de dependencias, como se muestra en el siguiente ejemplo. Obviamente, podemos añadir tantas convenciones como queramos:
services.AddMvc(options =>
{
   options.Conventions.Add(new MyCustomConvention());
   options.Conventions.Add(new MyOtherCustomConvention());
});
Veamos un ejemplo rápido. La siguiente convención personalizada elimina las acciones de un controlador en las que no se haya indicado expresamente el verbo HTTP a utilizar:
public class NoActionsWithoutVerbsConvention : IApplicationModelConvention
{
    public void Apply(ApplicationModel application)
    {
        foreach (var controller in application.Controllers)
        {
            var actionsToRemove = controller.Actions
                                     .Where(a => !a.HttpMethods.Any()).ToList();

            foreach (var action in actionsToRemove)
            {
                controller.Actions.Remove(action);
            }
        }
    }
}
Si registramos esta convención veremos que las acciones declaradas sin atributos como [HttpGet] o [HttpPost] dejarán de existir a todos los efectos. Simplemente hemos recorrido todos los controladores definidos en la aplicación, y hemos obtenido las acciones cuyos métodos HTTP no han sido definidos, eliminándolas a continuación.

Otro ejemplo sencillito. La siguiente convención se encargará automáticamente de añadir el filtro ValidateAntiForgeryToken a todas las acciones de la aplicación a las que se acceda utilizando el método POST:
public class AntiforgeryTokenInPostActionsConvention : IApplicationModelConvention
{
    public void Apply(ApplicationModel application)
    {
        var actions = application
            .Controllers.SelectMany(c => c.Actions)
            .Where(
                a => a.HttpMethods.Contains("POST")
                     && !a.Filters.Any(f => f is ValidateAntiForgeryTokenAttribute)
            );
        foreach (var action in actions)
        {
            action.Filters.Add(new ValidateAntiForgeryTokenAttribute());
        }
    }
}
Estoy seguro de que esto de las convenciones dará mucho juego, y sobre todo cuando empecemos a usarlas en el mundo real es cuando descubriremos su auténtico potencial. Pero de momento, la cosa ya promete ;)

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!