Este artículo proviene de un motor de traducción automática.

Tecnología de vanguardia

Filtros dinámicos de acción en ASP.NET MVC

Dino Esposito

Dino EspositoEl mes pasado hablé de la función y la implementación de filtros de acción en una aplicación de ASP.NET MVC. Para revisar un poco: Los filtros de acción son atributos que se utiliza para decorar el controlador de métodos y las clases con el fin de que realizan algunas acciones opcionales. Por ejemplo, podría escribir un atributo de compresión y que permite filtrar las respuestas generadas por el método a través de una secuencia gzip comprimidos de forma transparente. La ventaja principal es que la compresión código permanece aislada en una clase distinta y fácilmente reutilizable que contribuye a garantizar que las responsabilidades del método tan bajo como sea posible.

Los atributos, sin embargo, son algo estático. Para disfrutar de las ventajas de su flexibilidad inherente, tiene que ir a través de un paso de compilación adicionales. El cambio de los aspectos adicionales de la clase de controlador es fácil, pero se trata de la modificación del código de origen. En general, esto no es un gran inconveniente. La mayor parte del mantenimiento de código de trabajo pasa a través de físicos de los cambios realizados en el código fuente. Cuanto más se puede realizar estos cambios con eficacia y sin riesgos de la introducción de regresión, mejor.

(Sobre todo los portales de Web) de los sitios Web con contenido muy volátil y las características y de alto grado de personalización de software como una aplicación de servicio (SaaS), cualquier solución que evita tener que tocar el código de origen es más de bienvenida. ¿Por lo que la pregunta es, hay algo que puede hacer para cargar dinámicamente los filtros de la acción? Tal como se muestra en el resto de este artículo, la respuesta es Sí rotundo.

Dentro de ASP.NET MVC

El marco de trabajo de ASP.NET MVC expone un número de interfaces y métodos reemplazables que se pueden personalizar casi todos los aspectos de. En resumen, toda la colección de filtros de acción para un método de controlador se carga y se mantienen en una lista de la memoria. Como desarrollador, se le permita tener acceso y comprobar esta lista. Con algunas más trabajo, puede modificar la lista de filtros de acción y incluso rellenar dinámicamente.

Let’s tener una visión más detallada de cómo funciona una visión general de los pasos realizados por el marco de trabajo para realizar una acción. Asimismo, podrá cumplir con el componente central que permite filtros dinámicos cuyo manipulación: invocador del acción.

Invocador del acción es el responsable último de la ejecución de los métodos de acción en una clase de controlador. Invocador del acción implementa el interior del ciclo de vida de cada solicitud de ASP.NET MVC. La llamada es una instancia de una clase que implementa la interfaz IActionInvoker. Cada clase de controlador tiene su propio objeto invocador expuesto en el mundo a través de una propiedad get/set sin formato denominado ActionInvoker. La propiedad se define en el tipo de base System.Web.MVC.Controller como sigue:

public IActionInvoker ActionInvoker {

  get {

    if (this._actionInvoker == null) {

      this._actionInvoker = this.CreateActionInvoker();

    }

    return this._actionInvoker;

  }

  set {

    this._actionInvoker = value;

  }

}

El método CreateActionInvoker es un método reemplazable protegido del tipo de controlador. Ésta es su implementación:

protected virtual IActionInvoker CreateActionInvoker() {

  // Creates an instance of the built-in invoker

  return new ControllerActionInvoker();

}

Da la casualidad de que se puede cambiar el invocador de acción a voluntad para cualquier controlador. Sin embargo, debido a que la llamada está involucrado en bastante una fase temprana del ciclo de vida de solicitud, probablemente necesitará una fábrica de controladores para su propios invocador de llamada predeterminada de exchange. Junto con un marco de trabajo de la inversión de control (IoC) como Unity, este enfoque le permiten cambiar la lógica de la llamada directamente desde la configuración (sin conexión) del contenedor de IoC.

Como alternativa, puede definir una clase de base de controlador personalizado para su propia aplicación y reemplazar el método CreateActionInvoker para devolver simplemente el objeto invocador que necesita. Éste es el enfoque en el que el marco de trabajo de ASP.NET MVC se emplea para permitir la ejecución asincrónica de controlador de acciones.

Invocador del acción se basa en la interfaz IActionInvoker, que es bastante sencilla, tal como se expone un método:

public interface IActionInvoker {

  bool InvokeAction(

    ControllerContext controllerContext, 

    String actionName);

}

Let’s mantener tanto en el invocador de la acción predeterminada, revise las principales tareas que es responsable un invocador de la acción. En primer lugar, el invocador obtiene información sobre el controlador subyacente a la solicitud y la acción específica para llevar a cabo. Información que se obtiene a través de un objeto de descriptor de ad hoc. El descriptor incluye el nombre y el tipo del controlador, además de la lista de atributos y las acciones. Por motivos de rendimiento, llamada crea su propia caché de los descriptores de acción y el controlador.

Resulta interesante que eche un vistazo rápido al prototipo de la clase ControllerDescriptor en de figura 1. La clase representa sólo la clase base para cualquier descriptor real.

Figura 1 de La clase ControllerDescriptor

public abstract class ControllerDescriptor : 

  ICustomAttributeProvider {



  // Properties

  public virtual string ControllerName { get; }

  public abstract Type ControllerType { get; }



  // Method

  public abstract ActionDescriptor[] GetCanonicalActions();

  public virtual object[] GetCustomAttributes(bool inherit);

  public abstract ActionDescriptor FindAction(

    ControllerContext controllerContext, 

    string actionName);

  public virtual object[] GetCustomAttributes(

    Type attributeType, bool inherit);

  public virtual bool IsDefined(

    Type attributeType, bool inherit);

}

El marco de trabajo de ASP.NET MVC emplea dos clases de descriptor concretas que más se utilizan internamente la reflexión Microsoft .NET Framework. Uno se denomina ReflectedControllerDescriptor;el otro se utiliza sólo para los controladores asincrónicos y se denomina ReflectedAsyncControllerDescriptor.

Me apenas ocurren una situación real donde es posible que deba crear su propio descriptor. Sin embargo, para los que tiene curiosidad, let’s veamos cómo llevarlo a cabo.

Imagine que crea una clase derivada de descriptor y reemplace el método GetCanonicalActions para leer la lista de admitidos de acciones de un archivo de configuración o una tabla de base de datos. De esta forma, puede quitar métodos de acción válida en la lista basándose en la parte del contenido configurable. Para hacer este trabajo, sin embargo, hay que poner en su propio invocador de acción y escribir su método GetControllerDescriptor según corresponda para devolver una instancia de la descripción de personalizado:

protected virtual ControllerDescriptor 

  GetControllerDescriptor(

  ControllerContext controllerContext);

Obtener información sobre el método de controlador y acción es sólo el primer paso que lleva a cabo mediante el invocador de la acción. A continuación y más Curiosamente para los propósitos de este artículo, el invocador de acción obtiene la lista de filtros de acción para el método que se está procesando. Además, el invocador de acción comprueba los permisos de autorización del usuario, valida la solicitud con datos expuestos potencialmente peligrosas y, a continuación, llama al método.

Obteniendo la lista de filtros acción

Aunque el invocador de la acción se identifica con la interfaz IActionInvoker, el marco de trabajo de ASP.NET MVC utiliza los servicios de la clase integrada ControllerActionInvoker. Esta clase es compatible con muchos otros métodos y funciones, como los descriptores mencionadas anteriormente y filtros de la acción.

La clase ControllerActionInvoker ofrece dos puntos principales de intervención para manipular los filtros de la acción. Uno es el método GetFilters:

protected virtual ActionExecutedContext 

  InvokeActionMethodWithFilters(

  ControllerContext controllerContext, 

  IList<IActionFilter> filters, 

  ActionDescriptor actionDescriptor, 

  IDictionary<string, object> parameters);

El otro es el método InvokeActionMethodWithFilters:

protected virtual FilterInfo GetFilters(

  ControllerContext controllerContext, 

  ActionDescriptor actionDescriptor)

Ambos métodos se marcan protegidos y virtuales, como puede ver.

La llamada llama a GetFilters cuando sea necesario tener acceso a la lista de filtros definidos para una determinada acción. Como se puede suponer, esto se produce muy temprana del ciclo de vida de una solicitud y cualquier llamada al método InvokeActionMethodWithFilters anterior.

Tenga en cuenta que después de llamar a GetFilters, la llamada mantiene disponible toda la lista de filtros para cada categoría posible, incluidos los filtros de excepción, los filtros de resultados, filtros de autorización y, por supuesto, filtra la acción. Tenga en cuenta la siguiente clase de controlador:

[HandleError]

public class HomeController : Controller {

  public ActionResult About() {

    return View();

  }

}

Toda la clase está decorada con el atributo HandleError, que es un filtro de excepción, y no otro tipo de atributo es visible.

Ahora agregue un invocador personalizado let’s, reemplace el método GetFilters y coloque un punto de interrupción en la última línea del código, por ejemplo:

protected override FilterInfo GetFilters(

  ControllerContext controllerContext, 

  ActionDescriptor actionDescriptor) {



  var filters = base.GetFilters(

    controllerContext, actionDescriptor);

  return filters;

}

La figura 2 muestra el contenido real de los filtros de la variables.

image: Intercepting the Content of the Filters Collection

La figura 2 de interceptar el contenido de la colección de filtros

La clase FilterInfo: una clase pública en System.Web.Mvc—offers específico de colecciones de filtros para cada categoría:

public class FilterInfo {

  public IList<IActionFilter> ActionFilters { get; }

  public IList<IAuthorizationFilter> AuthorizationFilters { get; }

  public IList<IExceptionFilter> ExceptionFilters { get; }

  public IList<IResultFilter> ResultFilters { get; }

  ...
}

Como en de figura 2, para la clase trivial que se ha mostrado anteriormente cuenta una acción filtro, la autorización de un filtro, resultado de un filtro y dos filtros de excepción. ¿Quién definido los filtros de acción, el resultado y la autorización? La propia clase de controlador es un filtro de acción. De hecho, el controlador de clase base implementa todas las interfaces de filtro relacionados:

public abstract class Controller : 

    ControllerBase, IDisposable,

    IActionFilter, IAuthorizationFilter, 

    IExceptionFilter, IResultFilter {

    ...
}

La implementación base del GetFilters refleja los atributos de la clase de controlador utilizando la reflexión de .NET Framework. En la implementación del método GetFilters, puede agregar tantos filtros como desee, leerlos desde cualquier tipo de ubicación. Todo lo que necesita es un fragmento de código como éste:

protected override FilterInfo GetFilters(

  ControllerContext controllerContext, 

  ActionDescriptor actionDescriptor) {



  var filters = base.GetFilters(

    controllerContext, actionDescriptor);



  // Load additional filters

  var extraFilters = ReadFiltersFromConfig();

  filters.Add(extraFilters);



  return filters;

}

Este enfoque le ofrece la mayor flexibilidad y funciona para cualquier objetivo que desee conseguir, o con cualquier tipo de filtro que desee agregar.

Al invocar una acción

InvokeActionMethodWithFilters se invoca durante el proceso que tiene el rendimiento del método de acción. En este caso, el método recibe la lista de filtros de acción debe tener en cuenta. Sin embargo, aún está permitido agregar filtros adicionales en este momento. La figura 3 muestra una implementación de ejemplo InvokeActionMethodWithFilters que agrega dinámicamente un filtro de acción para comprimir el resultado. De figura 3 el código comprueba primero si el método invocado es concreta y, a continuación, se crea una instancia y se agrega un nuevo filtro. Hace falta decir que es posible determinar los filtros para cargar de forma que desee, incluida la lectura desde un archivo de configuración, una base de datos o cualquier otra cosa. Al reemplazar el método InvokeActionMethodWithFilters, que tiene que hacer es comprobar el método que se está ejecutando, anexar filtros de la acción adicional y llamar al método base para que pueda realizarse el invocador de forma habitual. Para recuperar información sobre el método que se está ejecutando, recurrir al contexto de controlador y el descriptor de la acción.

La figura 3 de Agregar un filtro de acción antes de ejecutar la acción

protected override ActionExecutedContext 

  InvokeActionMethodWithFilters(

  ControllerContext controllerContext, 

  IList<IActionFilter> filters, 

  ActionDescriptor actionDescriptor, 

  IDictionary<String, Object> parameters) {



  if (

    actionDescriptor.ControllerDescriptor.ControllerName == "Home" 

    && actionDescriptor.ActionName == "About") {



    var compressFilter = new CompressAttribute();

    filters.Add(compressFilter);

  }



  return base.InvokeActionMethodWithFilters(

    controllerContext, 

    filters, actionDescriptor, parameters);

}

Por lo tanto, tiene dos métodos posibles para agregar filtros de forma dinámica a una instancia de controlador: reemplazar el método GetFilters y reemplazar el método InvokeActionMethodWithFilters. Pero, ¿hay alguna diferencia?

El ciclo de vida de la acción

Pasar por GetFilters o InvokeActionMethodWithFilters es prácticamente lo mismo. Existen algunas diferencias, aunque no es ningún problema. Para comprender la diferencia, let’s obtener más información sobre los pasos que realiza el invocador de la acción predeterminada cuando se trata de ejecutar un método de acción. La figura 4 resume el ciclo de vida.

image: The Lifecycle of an Action Method

La figura 4 del ciclo de vida de un método de acción

Después de obtener los descriptores, llamada obtiene la lista de filtros y escribe en la fase de autorización. En este momento, la llamada se ocupa de los filtros de autorización que puede haber registrado. Si se produce un error en la autorización, cualquier resultado de la acción se ejecuta completamente sin tener en cuenta todos los filtros.

A continuación, en la llamada se comprueba si la solicitud requiere la validación de datos enviados y, a continuación, pasa a la ejecución del método de acción de cargar todos los filtros actualmente registrados.

Al final, si va a agregar dinámicamente los filtros de autorización, a continuación, sólo funcionará si lo hace a través del método GetFilters. Si su objetivo es agregar sólo filtros de acción, los filtros de resultados o los filtros de excepción, a continuación, utilizando cualquiera de estos métodos sólo produce el mismo resultado.

Filtros dinámicos

Carga dinámica de los filtros es una característica opcional que actúa principalmente de la finalidad de las aplicaciones con la volatilidad de una característica muy alto nivel. Un filtro, específicamente una acción de filtrar, habilita las capacidades de orientada a aspectos de una clase de controlador de ASP.NET MVC tal como permite activar y desactivar el comportamiento de forma declarativa.

Cuando se escribe el código fuente de una clase de controlador, puede agregar atributos de la acción a la clase o el nivel de método. Cuando lee acerca de los filtros de la acción de un origen de datos externos, la información de organización para que quede claro qué filtros se aplican a los métodos que no sea tan obvia. En un escenario de la base de datos, puede crear una tabla que utiliza el nombre de método y el controlador como la clave. En un escenario de configuración, probablemente deba trabajar fuera de una sección de configuración personalizada que proporciona la información que necesita. En cualquier caso, el marco de trabajo de ASP.NET MVC es lo suficientemente flexible como para que pueda decidir qué filtros se aplican en cada método y ni siquiera en una base de cada llamada.

Dino Esposito   es el autor de “ Programming ASP.NET MVC ” de Microsoft Press (2010). Con residencia en Italia, Esposito participa habitualmente en conferencias y eventos del sector en todo el mundo. Puede unirse a su blog en weblogs.asp.net/despos de .

Gracias al siguiente experto técnico para este artículo: Scott Hanselman