Filtros en ASP.NET Core

Por Kirk Larkin, Rick Anderson, Tom Dykstra y Steve Smith

Los filtros de ASP.NET Core permiten que el código se ejecute antes o después de fases específicas en la canalización de procesamiento de solicitudes.

Los filtros integrados se encargan de tareas como las siguientes:

  • Autorización, lo que impide el acceso a los recursos para los que un usuario no está autorizado.
  • Almacenamiento en caché de respuesta, cortocircuito de la canalización de solicitudes para devolver una respuesta almacenada en caché.

Se pueden crear filtros personalizados que se encarguen de cuestiones transversales. Entre los ejemplos de cuestiones transversales se incluyen el control de errores, el almacenamiento en caché, la configuración, la autorización y el registro. Los filtros evitan la duplicación de código. Así, por ejemplo, un filtro de excepción de control de errores puede consolidar el control de errores.

Este documento se aplica a Razor Pages, controladores de API y controladores con vistas. Los filtros no funcionan directamente con Razor los componentes. Un filtro solo puede afectar indirectamente a un componente cuando:

  • El componente está insertado en una página o vista.
  • La página o controlador y la vista usan el filtro.

Funcionamiento de los filtros

Los filtros se ejecutan dentro de la canalización de invocación de acciones de ASP.NET Core, a veces denominada canalización de filtro. La canalización de filtro se ejecuta después de ASP.NET Core selecciona la acción que se va a ejecutar:

La solicitud se procesa a través de otros middleware, middleware de enrutamiento, selección de acciones y canalización de invocación de acciones. El procesamiento de solicitudes continúa con la selección de acciones, el middleware de enrutamiento y otros middleware antes de convertirse en una respuesta enviada al cliente.

Tipos de filtro

Cada tipo de filtro se ejecuta en una fase diferente dentro de la canalización de filtro:

  • Filtros de autorización:

    • Ejecute primero.
    • Determine si el usuario está autorizado para la solicitud.
    • Cortocircuite la canalización si la solicitud no está autorizada.
  • Filtros de recursos:

    • Se ejecutan después de la autorización.
    • OnResourceExecuting ejecuta código antes que el resto de la canalización del filtro. Por ejemplo, OnResourceExecuting ejecuta código antes que el enlace de modelos.
    • OnResourceExecuted ejecuta el código una vez que el resto de la canalización se haya completado.
  • Filtros de acción:

    • Ejecute inmediatamente antes y después de llamar a un método de acción.
    • Pueden cambiar los argumentos pasados a una acción.
    • Pueden cambiar el resultado devuelto de la acción.
    • No se admiten en Razor Pages.
  • Los filtros de excepciones aplican directivas globales a las excepciones no controladas que se producen antes de que se escriba el cuerpo de respuesta.

  • Filtros de resultados:

    • Ejecute inmediatamente antes y después de la ejecución de los resultados de la acción.
    • Solo se ejecuta cuando el método de acción se ejecuta correctamente.
    • Son útiles para la lógica que debe rodear la ejecución de la vista o del formateador.

En el diagrama siguiente se muestra cómo interactúan los tipos de filtro en la canalización de filtro:

La solicitud se procesa mediante filtros de autorización, filtros de recursos, enlace de modelos, filtros de acción, ejecución de acciones y conversión de resultados de acción, filtros de excepciones, filtros de resultados y ejecución de resultados. Al salir, la solicitud solo se procesa mediante filtros de resultados y filtros de recursos antes de convertirse en una respuesta enviada al cliente.

Razor Las páginas también admiten Razor filtros de página, que se ejecutan antes y después de un Razor controlador de páginas.

Implementación

Los filtros admiten implementaciones tanto sincrónicas como asincrónicas a través de diferentes definiciones de interfaz.

Los filtros sincrónicos se ejecutan antes y después de su fase de canalización. Por ejemplo, OnActionExecuting se llama antes de llamar al método de acción. OnActionExecuted se llama después de que el método de acción devuelva:

public class SampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

Los filtros asincrónicos definen un método On-Stage-ExecutionAsync. Por ejemplo, OnActionExecutionAsync:

public class SampleAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // Do something before the action executes.
        await next();
        // Do something after the action executes.
    }
}

En el código anterior, SampleAsyncActionFilter tiene , ActionExecutionDelegatenext, que ejecuta el método de acción .

Varias fases de filtro

Se pueden implementar interfaces para varias fases de filtro en una sola clase. Por ejemplo, la clase ActionFilterAttribute implementa:

Implemente la versión sincrónica o la versión asincrónica de una interfaz de filtro, pero no ambas. El entorno de ejecución comprueba primero si el filtro implementa la interfaz asincrónica y, si es así, llama a la interfaz. De lo contrario, llamará a métodos de interfaz sincrónicos. Si se implementan las interfaces asincrónicas y sincrónicas en una clase, solo se llama al método asincrónico. Cuando se usan clases abstractas como ActionFilterAttribute, invalide solo los métodos sincrónicos o los métodos asincrónicos para cada tipo de filtro.

Atributos de filtros integrados

ASP.NET Core incluye filtros integrados basados en atributos que se pueden personalizar y a partir de los cuales crear subclases. Por ejemplo, el siguiente filtro de resultados agrega un encabezado a la respuesta:

public class ResponseHeaderAttribute : ActionFilterAttribute
{
    private readonly string _name;
    private readonly string _value;

    public ResponseHeaderAttribute(string name, string value) =>
        (_name, _value) = (name, value);

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, _value);

        base.OnResultExecuting(context);
    }
}

Los atributos permiten a los filtros aceptar argumentos, como se muestra en el ejemplo anterior. Aplique el ResponseHeaderAttribute a un método de acción o controlador y especifique el nombre y el valor del encabezado HTTP:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
    public IActionResult Index() =>
        Content("Examine the response headers using the F12 developer tools.");

    // ...

Use una herramienta como las herramientas de desarrollo del explorador para examinar los encabezados. En Encabezados de respuesta, se muestra filter-header: Filter Value.

El código siguiente se aplica ResponseHeaderAttribute tanto a un controlador como a una acción:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
    public IActionResult Index() =>
        Content("Examine the response headers using the F12 developer tools.");

    // ...

    [ResponseHeader("Another-Filter-Header", "Another Filter Value")]
    public IActionResult Multiple() =>
        Content("Examine the response headers using the F12 developer tools.");
}

Las respuestas de la Multiple acción incluyen los siguientes encabezados:

  • filter-header: Filter Value
  • another-filter-header: Another Filter Value

Algunas de las interfaces de filtro tienen atributos correspondientes que se pueden usar como clases base en las implementaciones personalizadas.

Atributos de filtro:

Los filtros no se pueden aplicar a Razor los métodos de controlador de páginas. Se pueden aplicar al Razor modelo de página o globalmente.

Ámbitos del filtro y orden de ejecución

Un filtro se puede agregar a la canalización en uno de tres ámbitos posibles:

  • Usar un atributo en un controlador o Razor page.
  • Mediante un atributo en una acción del controlador. Los atributos de filtro no se pueden aplicar a Razor los métodos de controlador de Pages.
  • Globalmente para todos los controladores, acciones y Razor Páginas, como se muestra en el código siguiente:
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.AddControllersWithViews(options =>
    {
        options.Filters.Add<GlobalSampleActionFilter>();
    });
    

Orden de ejecución predeterminado

Cuando hay varios filtros en una determinada fase de la canalización, el ámbito determina el orden predeterminado en el que esos filtros se van a ejecutar. Los filtros globales abarcan a los filtros de clase, que a su vez engloban a los filtros de método.

Como resultado de este anidamiento de filtros, el código de filtros posterior se ejecuta en el orden inverso al código anterior. La secuencia de filtro:

  • El código anterior de los filtros globales.
    • El código anterior de los filtros de controlador.
      • El código anterior de los filtros de métodos de acción.
      • El código posterior de los filtros de métodos de acción.
    • El código posterior de los filtros de controlador.
  • El código posterior de los filtros globales.

En el ejemplo siguiente se muestra el orden en el que se ejecutan los métodos de filtro para los filtros de acción sincrónica:

Secuencia Ámbito del filtro Método de filtro
1 Global OnActionExecuting
2 Controller OnActionExecuting
3 Acción OnActionExecuting
4 Acción OnActionExecuted
5 Controller OnActionExecuted
6 Global OnActionExecuted

Filtros de nivel de controlador

Cada controlador que hereda de Controller incluye los OnActionExecutingmétodos , OnActionExecutionAsyncy OnActionExecuted . Estos métodos encapsulan los filtros que se ejecutan para una acción determinada:

  • OnActionExecuting se ejecuta antes de cualquiera de los filtros de la acción.
  • OnActionExecuted se ejecuta después de todos los filtros de la acción.
  • OnActionExecutionAsync se ejecuta antes de cualquiera de los filtros de la acción. Código después de que se ejecute una llamada a next después de los filtros de la acción.

La clase siguiente ControllerFiltersController :

  • Aplica (SampleActionFilterAttribute[SampleActionFilter]) al controlador.
  • Invalida OnActionExecuting y OnActionExecuted.
[SampleActionFilter]
public class ControllerFiltersController : Controller
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuting)}");

        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuted)}");

        base.OnActionExecuted(context);
    }

    public IActionResult Index()
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(Index)}");

        return Content("Check the Console.");
    }
}

Si se dirige a https://localhost:<port>/ControllerFilters, se ejecuta el código siguiente:

  • ControllerFiltersController.OnActionExecuting
    • GlobalSampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • ControllerFiltersController.Index
      • SampleActionFilterAttribute.OnActionExecuted
    • GlobalSampleActionFilter.OnActionExecuted
  • ControllerFiltersController.OnActionExecuted

Los filtros de nivel de controlador establecen la propiedad Order en int.MinValue. Los filtros de nivel de controlador no pueden establecerse para ejecutarse tras aplicarse los filtros a los métodos. Order se explica en la sección siguiente.

Para Pages Razor , consulte Implementar Razor filtros de página invalidando métodos de filtro.

Invalidación del orden predeterminado

La secuencia de ejecución predeterminada se puede invalidar con la implementación de IOrderedFilter. Order expone la propiedad IOrderedFilter que tiene prioridad sobre el ámbito a la hora de determinar el orden de ejecución. Un filtro con un valor Order menor:

  • Ejecuta el código anterior antes que el de un filtro con un valor mayor de Order.
  • Ejecuta el código posterior después que el de un filtro con un valor mayor de Order.

En el ejemplo de filtros de nivel de controlador , GlobalSampleActionFilter tiene ámbito global, por lo que se ejecuta antes SampleActionFilterAttributede , que tiene ámbito de controlador. Para que la ejecución se SampleActionFilterAttribute ejecute primero, establezca su orden en int.MinValue:

[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
    // ...
}

Para que el filtro GlobalSampleActionFilter global se ejecute primero, establezca su en Orderint.MinValue:

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});

Cancelación y cortocircuito

La canalización de filtro se puede cortocircuitar en cualquier momento mediante el establecimiento de la propiedad Result en el parámetro ResourceExecutingContext que se ha proporcionado al método de filtro. Por ejemplo, el siguiente filtro de recursos impide que el resto de la canalización se ejecute:

public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        context.Result = new ContentResult
        {
            Content = nameof(ShortCircuitingResourceFilterAttribute)
        };
    }

    public void OnResourceExecuted(ResourceExecutedContext context) { }
}

En el siguiente código, tanto el filtro [ShortCircuitingResourceFilter] como el filtro [ResponseHeader] tienen como destino el método de acción Index. Filtro ShortCircuitingResourceFilterAttribute :

  • Se ejecuta en primer lugar, porque es un filtro de recursos y ResponseHeaderAttribute es un filtro de acciones.
  • Cortocircuita el resto de la canalización.

Por tanto, el filtro ResponseHeaderAttribute nunca se ejecuta en relación con la acción Index. Este comportamiento sería el mismo si ambos filtros se aplicaran en el nivel de método de acción, siempre y cuando ShortCircuitingResourceFilterAttribute se haya ejecutado primero. Primero ShortCircuitingResourceFilterAttribute se ejecuta debido a su tipo de filtro:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
    [ShortCircuitingResourceFilter]
    public IActionResult Index() =>
        Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}

Inserción de dependencias

Los filtros se pueden agregar por tipo o por instancia. Si se agrega una instancia, esta se utiliza para todas las solicitudes. Si se agrega un tipo, se activa por tipo. Un filtro activado por tipo significa:

Los filtros que se implementan como atributos y se agregan directamente a las clases de controlador o a los métodos de acción no pueden tener dependencias de constructor proporcionadas por la inserción de dependencias. No se pueden proporcionar dependencias de constructor mediante inserción de dependencias porque los atributos deben tener sus parámetros de constructor proporcionados donde se aplican.

Los filtros siguientes admiten dependencias de constructor proporcionadas en la inserción de dependencias:

Los filtros anteriores se pueden aplicar a un controlador o a una acción.

Los registradores están disponibles en la inserción de dependencias. Sin embargo, evite crear y utilizar filtros únicamente con fines de registro. El registro del marco integrado proporciona normalmente lo que se necesita para el registro. Registro agregado a los filtros:

  • Debe centrarse en cuestiones de dominio empresarial o en el comportamiento específico del filtro.
  • No debe registrar acciones u otros eventos del marco. Los filtros integrados ya registran acciones y eventos de marco.

ServiceFilterAttribute

Los tipos de implementación de filtro de servicio se registran en Program.cs. ServiceFilterAttribute recupera una instancia del filtro de la inserción de dependencias.

En el código siguiente se muestra la clase , que usa la LoggingResponseHeaderFilterService inserción de dependencias:

public class LoggingResponseHeaderFilterService : IResultFilter
{
    private readonly ILogger _logger;

    public LoggingResponseHeaderFilterService(
            ILogger<LoggingResponseHeaderFilterService> logger) =>
        _logger = logger;

    public void OnResultExecuting(ResultExecutingContext context)
    {
        _logger.LogInformation(
            $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}");

        context.HttpContext.Response.Headers.Add(
            nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService));
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        _logger.LogInformation(
            $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}");
    }
}

En el código siguiente, LoggingResponseHeaderFilterService se agrega al contenedor de inserción de dependencias:

builder.Services.AddScoped<LoggingResponseHeaderFilterService>();

En el código siguiente, el atributo ServiceFilter recupera una instancia del filtro LoggingResponseHeaderFilterService desde la inserción de dependencias:

[ServiceFilter(typeof(LoggingResponseHeaderFilterService))]
public IActionResult WithServiceFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");

Al usar ServiceFilterAttribute, establezca ServiceFilterAttribute.IsReusable:

  • Proporciona una sugerencia que la instancia de filtro podría reutilizarse fuera del ámbito de la solicitud en la que se creó. El entorno de ejecución de ASP.NET Core no garantiza:
    • Que se creará una única instancia del filtro.
    • El filtro no volverá a solicitarse desde el contenedor de inserción de dependencias en algún momento posterior.
  • No se debe usar con un filtro que dependa de los servicios con una duración distinta de singleton.

ServiceFilterAttribute implementa IFilterFactory. IFilterFactory expone el método CreateInstance para crear una instancia de IFilterMetadata. CreateInstance carga el tipo especificado desde la inserción de dependencias.

TypeFilterAttribute

TypeFilterAttribute es similar a ServiceFilterAttribute, pero su tipo no se resuelve directamente desde el contenedor de inserción de dependencias, sino que crea una instancia del tipo usando el elemento Microsoft.Extensions.DependencyInjection.ObjectFactory.

Dado que los tipos TypeFilterAttribute no se resuelven directamente desde el contenedor de inserción de dependencias:

  • Los tipos a los que se hace referencia con TypeFilterAttribute no tienen que estar registrados con el contenedor de inserción de dependencias. Sus dependencias se completan a través del contenedor de inserción de dependencias.
  • TypeFilterAttribute puede aceptar opcionalmente argumentos de constructor del tipo en cuestión.

Al usar TypeFilterAttribute, establezca TypeFilterAttribute.IsReusable:

  • Proporciona una sugerencia que indica que la instancia de filtro podría reutilizarse fuera del ámbito de la solicitud en la que se creó. El entorno de ejecución de ASP.NET Core no ofrece ninguna garantía de que se vaya a crear una única instancia del filtro.

  • No debe usarse con un filtro que depende de servicios con una duración distinta de singleton.

En el siguiente ejemplo se muestra cómo pasar argumentos a un tipo mediante TypeFilterAttribute:

[TypeFilter(typeof(LoggingResponseHeaderFilter),
    Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");

Filtros de autorización

Filtros de autorización:

  • Son los primeros filtros que se ejecutan en la canalización del filtro.
  • Controlan el acceso a los métodos de acción.
  • Tienen un método anterior, pero no uno posterior.

Los filtros de autorización personalizados requieren un marco de autorización personalizado. Es preferible configurar directivas de autorización o escribir una directiva de autorización personalizada a escribir un filtro personalizado. El filtro de autorización integrado:

  • Llama a la autorización del sistema.
  • No autoriza las solicitudes.

No inicie excepciones dentro de los filtros de autorización:

  • La excepción no se controlará.
  • Los filtros de excepciones no controlarán la excepción.

Considere la posibilidad de emitir un desafío cuando se produzca una excepción en un filtro de autorizaciones.

Aquí encontrará más información sobre la autorización.

Filtros de recursos

Filtros de recursos:

Los filtros de recursos son útiles para cortocircuitar la mayor parte de la canalización. Por ejemplo, un filtro de almacenamiento en caché puede evitar que se ejecute el resto de la canalización en un acierto de caché.

Ejemplos de filtros de recursos:

Filtros de acciones

Los filtros de acción no se aplican a Razor Pages. Razor Pages admite IPageFilter y IAsyncPageFilter . Para obtener más información, vea Métodos de filtrado de Razor Pages.

Filtros de acciones:

El código siguiente muestra un ejemplo de filtro de acciones:

public class SampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

ActionExecutingContext ofrece las siguientes propiedades:

  • ActionArguments: permite leer las entradas de un método de acción.
  • Controller: permite manipular la instancia del controlador.
  • Result: si se establece Result, se cortocircuita la ejecución del método de acción y de los filtros de acciones posteriores.

Inicio de una excepción en un método de acción:

  • Impide la ejecución de los filtros subsiguientes.
  • A diferencia del establecimiento de Result, se trata como un error en lugar de como un resultado correcto.

ActionExecutedContext proporciona Controller y Result, además de las siguientes propiedades:

  • Canceled: es true si otro filtro ha cortocircuitado la ejecución de la acción.
  • Exception: es un valor distinto de NULL si la acción o un filtro de acción de ejecución anterior han producido una excepción. Si se establece esta propiedad en un valor NULL:
    • Controla la excepción eficazmente.
    • Result se ejecuta como si se devolviera desde el método de acción.

En un IAsyncActionFilter, una llamada a ActionExecutionDelegate:

  • Ejecuta cualquier filtro de acciones posterior y el método de acción.
  • Devuelve ActionExecutedContext.

Para cortocircuitar esto, asigne Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result a una instancia de resultado y no llame a next (la clase ActionExecutionDelegate).

El marco proporciona una clase ActionFilterAttribute abstracta de la que se pueden crear subclases.

Se puede usar el filtro de acción OnActionExecuting para:

  • Validar el estado del modelo.
  • Devolver un error si el estado no es válido.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}

Nota

Los controladores anotados con el atributo validan automáticamente el estado del [ApiController] modelo y devuelven una respuesta 400. Para obtener más información, consulte Respuestas HTTP 400 automáticas.

El método OnActionExecuted se ejecuta después del método de acción:

  • Y puede ver y manipular los resultados de la acción a través de la propiedad Result.
  • Canceled se establece en true si otro filtro ha cortocircuitado la ejecución de la acción.
  • Exception se establece en un valor distinto de NULL si la acción o un filtro de acción posterior han producido una excepción. Si Exception se establece como nulo:
    • Controla una excepción eficazmente.
    • ActionExecutedContext.Result se ejecuta como si se devolviera con normalidad desde el método de acción.

Filtros de excepciones

Los filtros de excepciones:

En el siguiente filtro de excepción de ejemplo se muestran detalles sobre las excepciones que se producen cuando la aplicación está en desarrollo:

public class SampleExceptionFilter : IExceptionFilter
{
    private readonly IHostEnvironment _hostEnvironment;

    public SampleExceptionFilter(IHostEnvironment hostEnvironment) =>
        _hostEnvironment = hostEnvironment;

    public void OnException(ExceptionContext context)
    {
        if (!_hostEnvironment.IsDevelopment())
        {
            // Don't display exception details unless running in Development.
            return;
        }

        context.Result = new ContentResult
        {
            Content = context.Exception.ToString()
        };
    }
}

El código siguiente prueba el filtro de excepciones:

[TypeFilter(typeof(SampleExceptionFilter))]
public class ExceptionController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}

Los filtros de excepciones:

  • No tienen eventos anteriores ni posteriores.
  • Implementan OnException o OnExceptionAsync.
  • Controle las excepciones no controladas que se producen en Razor la creación de páginas o controladores, el enlace de modelos, los filtros de acción o los métodos de acción.
  • No detectan aquellas excepciones que se produzcan en los filtros de recursos, en los filtros de resultados o en la ejecución de resultados de MVC.

Para controlar una excepción, establezca la ExceptionHandled propiedad true en o asigne la Result propiedad . Esto detiene la propagación de la excepción. Un filtro de excepciones no tiene capacidad para convertir una excepción en un proceso "correcto". Eso solo lo pueden hacer los filtros de acciones.

Los filtros de excepciones:

  • Son adecuados para interceptar las excepciones que se producen en las acciones.
  • No son tan flexibles como el middleware de control de errores.

Es preferible usar middleware de control de excepciones. Utilice los filtros de excepciones solo cuando el control de errores es diferente en función del método de acción que se llama. Por ejemplo, puede que una aplicación tenga métodos de acción tanto para los puntos de conexión de API como para las vistas/HTML. Los puntos de conexión de API podrían devolver información de error como JSON, mientras que las acciones basadas en vistas podrían devolver una página de error como HTML.

Filtros de resultados

Filtros de resultados:

IResultFilter e IAsyncResultFilter

En el código siguiente se muestra un filtro de resultados de ejemplo:

public class SampleResultFilter : IResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        // Do something before the result executes.
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Do something after the result executes.
    }
}

El tipo de resultado que se ejecute dependerá de la acción. Una acción que devuelve una vista incluye todo el procesamiento de Razor como parte del elemento ViewResult que se está ejecutando. Un método API puede llevar a cabo algunas funciones de serialización como parte de la ejecución del resultado. Aquí encontrará más información sobre los resultados de acciones.

Los filtros de resultados solo se ejecutan cuando una acción o un filtro de acción genera un resultado de acción. Los filtros de resultados no se ejecutan cuando:

  • Un filtro de autorización o un filtro de recursos genera un cortocircuito en la canalización.
  • Un filtro de excepciones controla una excepción al producir un resultado de acción.

El método Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting puede cortocircuitar la ejecución del resultado de la acción y de los filtros de resultados posteriores mediante el establecimiento de Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel en true. Escriba en el objeto de respuesta cuando el proceso se cortocircuite, ya que así evitará que se genere una respuesta vacía. Generación de una excepción en IResultFilter.OnResultExecuting:

  • Impide la ejecución del resultado de la acción y de los filtros subsiguientes.
  • Se trata como un error en lugar de como un resultado correcto.

Cuando se ejecuta el método Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted, es probable que la respuesta ya se haya enviado al cliente. En este caso, no se puede cambiar.

ResultExecutedContext.Canceled se establece en true si otro filtro ha cortocircuitado la ejecución del resultado de la acción.

ResultExecutedContext.Exception se establece en un valor distinto de NULL si el resultado de la acción o un filtro de resultado posterior ha producido una excepción. Establecer Exception en un valor null hace que una excepción se controle de forma eficaz y evita que se vuelva a producir dicha excepción más adelante en la canalización. No hay una forma confiable de escribir datos en una respuesta cuando se controla una excepción en un filtro de resultados. Si los encabezados ya se han vaciado en el cliente si el resultado de una acción inicia una excepción, no hay ningún mecanismo confiable que permita enviar un código de error.

En un elemento IAsyncResultFilter, una llamada a await next en ResultExecutionDelegate ejecuta cualquier filtro de resultados posterior y el resultado de la acción. Para cortocircuito, establezca ResultExecutingContext.Cancel en true y no llame a ResultExecutionDelegate:

public class SampleAsyncResultFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(
        ResultExecutingContext context, ResultExecutionDelegate next)
    {
        if (context.Result is not EmptyResult)
        {
            await next();
        }
        else
        {
            context.Cancel = true;
        }
    }
}

El marco proporciona una clase ResultFilterAttribute abstracta de la que se pueden crear subclases. La clase ResponseHeaderAttribute mostrada anteriormente es un ejemplo de un atributo de filtro de resultados.

IAlwaysRunResultFilter e IAsyncAlwaysRunResultFilter

Las interfaces IAlwaysRunResultFilter e IAsyncAlwaysRunResultFilter declaran una implementación IResultFilter que se ejecuta para obtener todos los resultados de la acción. Esto incluye los resultados de la acción generados por:

  • Filtros de autorización y filtros de recursos que generan un cortocircuito.
  • Filtros de excepción.

Por ejemplo, el siguiente filtro siempre ejecuta y establece un resultado de la acción (ObjectResult) con un código de estado 422 - Entidad no procesable cuando se produce un error en la negociación de contenido:

public class UnprocessableResultFilter : IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is StatusCodeResult statusCodeResult
            && statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType)
        {
            context.Result = new ObjectResult("Unprocessable")
            {
                StatusCode = StatusCodes.Status422UnprocessableEntity
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context) { }
}

IFilterFactory

IFilterFactory implementa IFilterMetadata. Por tanto, una instancia de IFilterFactory se puede usar como una instancia de IFilterMetadata en cualquier parte de la canalización de filtro. Cuando el entorno de ejecución se prepara para invocar el filtro, intenta convertirlo a un IFilterFactory. Si esa conversión se realiza correctamente, se llama al método CreateInstance para crear la instancia IFilterMetadata que se va a invocar. Esto proporciona un diseño flexible, dado que no hay que establecer la canalización de filtro exacta de forma explícita cuando la aplicación se inicia.

IFilterFactory.IsReusable:

  • Es una sugerencia de la fábrica en la que se puede reutilizar la instancia de filtro creada por el generador fuera del ámbito de solicitud en el que se creó.
  • No se debe usar con un filtro que dependa de los servicios con una duración distinta de singleton.

El entorno de ejecución de ASP.NET Core no garantiza:

  • Que se creará una única instancia del filtro.
  • El filtro no volverá a solicitarse desde el contenedor de inserción de dependencias en algún momento posterior.

Advertencia

IFilterFactory.IsReusable Configure solo para devolver true si el origen de los filtros no es ambiguo, los filtros no tienen estado y los filtros son seguros para usar en varias solicitudes HTTP. Por ejemplo, no devuelva filtros de DI registrados como ámbito o transitorios si IFilterFactory.IsReusable devuelve true.

Puede implementar IFilterFactory con las implementaciones de atributos personalizados como método alternativo para crear filtros:

public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
        new InternalResponseHeaderFilter();

    private class InternalResponseHeaderFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context) =>
            context.HttpContext.Response.Headers.Add(
                nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));

        public void OnActionExecuted(ActionExecutedContext context) { }
    }

El filtro se aplica en el código siguiente:

[ResponseHeaderFilterFactory]
public IActionResult Index() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");

IFilterFactory implementado en un atributo

Los filtros que implementan IFilterFactory son útiles para los filtros que:

  • No requieren pasar parámetros.
  • Tienen dependencias de constructor que deben completarse por medio de la inserción de dependencias.

TypeFilterAttribute implementa IFilterFactory. IFilterFactory expone el método CreateInstance para crear una instancia de IFilterMetadata. CreateInstance carga el tipo especificado desde el contenedor de servicios (inserción de dependencias).

public class SampleActionTypeFilterAttribute : TypeFilterAttribute
{
    public SampleActionTypeFilterAttribute()
         : base(typeof(InternalSampleActionFilter)) { }

    private class InternalSampleActionFilter : IActionFilter
    {
        private readonly ILogger<InternalSampleActionFilter> _logger;

        public InternalSampleActionFilter(ILogger<InternalSampleActionFilter> logger) =>
            _logger = logger;

        public void OnActionExecuting(ActionExecutingContext context)
        {
            _logger.LogInformation(
                $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuting)}");
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _logger.LogInformation(
                $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuted)}");
        }
    }
}

En el código siguiente se muestran tres enfoques para aplicar el filtro:

[SampleActionTypeFilter]
public IActionResult WithDirectAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}");

[TypeFilter(typeof(SampleActionTypeFilterAttribute))]
public IActionResult WithTypeFilterAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}");

[ServiceFilter(typeof(SampleActionTypeFilterAttribute))]
public IActionResult WithServiceFilterAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}");

En el código anterior, se prefiere el primer enfoque para aplicar el filtro.

Uso del middleware en la canalización de filtro

Los filtros de recursos funcionan como el middleware, en el sentido de que se encargan de la ejecución de todo lo que viene después en la canalización. Pero los filtros se diferencian del middleware en que forman parte del runtime, lo que significa que tienen acceso al contexto y las construcciones.

Para usar middleware como un filtro, cree un tipo con un método Configure en el que se especifique el middleware para insertar en la canalización de filtro. En el ejemplo siguiente se usa middleware para establecer un encabezado de respuesta:

public class FilterMiddlewarePipeline
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            context.Response.Headers.Add("Pipeline", "Middleware");

            await next();
        });
    }
}

Use MiddlewareFilterAttribute para ejecutar el middleware:

[MiddlewareFilter(typeof(FilterMiddlewarePipeline))]
public class FilterMiddlewareController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}

Los filtros de middleware se ejecutan en la misma fase de la canalización de filtro que los filtros de recursos, antes del enlace de modelos y después del resto de la canalización.

Seguridad para subprocesos

Al pasar una instancia de un filtro a Add, en lugar de su Type, el filtro es un singleton y no es seguro para subprocesos.

Recursos adicionales

Por Kirk Larkin, Rick Anderson, Tom Dykstra y Steve Smith

Los filtros en ASP.NET Core permiten que se ejecute el código antes o después de determinadas fases de la canalización del procesamiento de la solicitud.

Los filtros integrados se encargan de tareas como las siguientes:

  • Autorización, lo que impide el acceso a los recursos para los que un usuario no está autorizado.
  • Almacenamiento en caché de respuestas, cortocircuito de la canalización de solicitudes para devolver una respuesta almacenada en caché.

Se pueden crear filtros personalizados que se encarguen de cuestiones transversales. Entre los ejemplos de cuestiones transversales se incluyen el control de errores, el almacenamiento en caché, la configuración, la autorización y el registro. Los filtros evitan la duplicación de código. Así, por ejemplo, un filtro de excepción de control de errores puede consolidar el control de errores.

Este documento se aplica a Razor Pages, controladores de API y controladores con vistas. Los filtros no funcionan directamente con Razor los componentes. Un filtro solo puede afectar indirectamente a un componente cuando:

  • El componente está insertado en una página o vista.
  • La página o controlador y la vista usan el filtro.

Vea o descargue el ejemplo (cómo descargarlo).

Funcionamiento de los filtros

Los filtros se ejecutan dentro de la canalización de invocación de acciones de ASP.NET Core, a veces denominada canalización de filtro. La canalización de filtro se ejecuta después de que ASP.NET Core seleccione la acción que se va a ejecutar.

La solicitud se procesa a través de otros middleware, middleware de enrutamiento, selección de acciones y canalización de invocación de acciones. El procesamiento de solicitudes continúa a través de la selección de acciones, el middleware de enrutamiento y otro middleware antes de convertirse en una respuesta enviada al cliente.

Tipos de filtro

Cada tipo de filtro se ejecuta en una fase diferente dentro de la canalización de filtro:

  • Los filtros de autorización se ejecutan en primer lugar y sirven para averiguar si el usuario está autorizado para realizar la solicitud. Los filtros de autorización pueden cortocircuitar la canalización si una solicitud no está autorizada.

  • Filtros de recursos:

    • Se ejecutan después de la autorización.
    • OnResourceExecuting ejecuta código antes que el resto de la canalización del filtro. Por ejemplo, OnResourceExecuting ejecuta código antes que el enlace de modelos.
    • OnResourceExecuted ejecuta el código una vez que el resto de la canalización se haya completado.
  • Filtros de acción:

    • Ejecutan código inmediatamente antes y después de llamar a un método de acción.
    • Pueden cambiar los argumentos pasados a una acción.
    • Pueden cambiar el resultado devuelto de la acción.
    • No se admiten en Razor Pages.
  • Los filtros de excepciones aplican directivas globales a las excepciones no controladas que se producen antes de que se escriba el cuerpo de respuesta.

  • Los filtros de resultados ejecutan código inmediatamente antes y después de la ejecución de resultados de acción. Se ejecutan solo cuando el método de acción se ha ejecutado correctamente. Son útiles para la lógica que debe regir la ejecución de la vista o el formateador.

En el siguiente diagrama se muestra cómo interactúan los tipos de filtro en la canalización de filtro.

La solicitud se procesa a través de filtros de autorización, filtros de recursos, enlace de modelos, filtros de acción, ejecución de acciones y conversión de resultados de acción, filtros de excepción, filtros de resultados y ejecución de resultados. Al salir, la solicitud solo se procesa mediante filtros de resultados y filtros de recursos antes de convertirse en una respuesta enviada al cliente.

Implementación

Los filtros admiten implementaciones tanto sincrónicas como asincrónicas a través de diferentes definiciones de interfaz.

Los filtros sincrónicos ejecutan código antes y después de la fase de canalización. Por ejemplo, OnActionExecuting se llama antes de llamar al método de acción. OnActionExecuted se llama después de devolver el método de acción.

public class MySampleActionFilter : IActionFilter 
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }
}

En el código anterior, MyDebug es una función de utilidad en la descarga de ejemplo.

Los filtros asincrónicos definen un método On-Stage-ExecutionAsync. Por ejemplo, OnActionExecutionAsync:

public class SampleAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context,
        ActionExecutionDelegate next)
    {
        // Do something before the action executes.

        // next() calls the action method.
        var resultContext = await next();
        // resultContext.Result is set.
        // Do something after the action executes.
    }
}

En el código anterior, SampleAsyncActionFilter tiene un delegado ActionExecutionDelegate (next) que ejecuta el método de acción.

Varias fases de filtro

Se pueden implementar interfaces para varias fases de filtro en una sola clase. Por ejemplo, la clase ActionFilterAttribute implementa:

Implemente la versión sincrónica o la versión asincrónica de una interfaz de filtro, pero no ambas. El entorno de ejecución comprueba primero si el filtro implementa la interfaz asincrónica y, si es así, llama a la interfaz. De lo contrario, llamará a métodos de interfaz sincrónicos. Si se implementan las interfaces asincrónicas y sincrónicas en una clase, solo se llama al método asincrónico. Al usar clases abstractas como ActionFilterAttribute, invalide solo los métodos sincrónicos o los métodos asincrónicos para cada tipo de filtro.

Atributos de filtros integrados

ASP.NET Core incluye filtros integrados basados en atributos que se pueden personalizar y a partir de los cuales crear subclases. Por ejemplo, el siguiente filtro de resultados agrega un encabezado a la respuesta:

public class AddHeaderAttribute : ResultFilterAttribute
{
    private readonly string _name;
    private readonly string _value;

    public AddHeaderAttribute(string name, string value)
    {
        _name = name;
        _value = value;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add( _name, new string[] { _value });
        base.OnResultExecuting(context);
    }
}

Los atributos permiten a los filtros aceptar argumentos, como se muestra en el ejemplo anterior. Aplique el AddHeaderAttribute a un método de acción o controlador y especifique el nombre y el valor del encabezado HTTP:

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

Use una herramienta como las herramientas de desarrollo del explorador para examinar los encabezados. En Encabezados de respuesta, se muestra author: Rick Anderson.

El código siguiente implementa un atributo ActionFilterAttribute que:

  • Lee el título y el nombre del sistema de configuración. A diferencia del ejemplo anterior, el siguiente código no requiere que se agreguen parámetros de filtro al código.
  • Agrega el título y el nombre al encabezado de respuesta.
public class MyActionFilterAttribute : ActionFilterAttribute
{
    private readonly PositionOptions _settings;

    public MyActionFilterAttribute(IOptions<PositionOptions> options)
    {
        _settings = options.Value;
        Order = 1;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_settings.Title, 
                                                 new string[] { _settings.Name });
        base.OnResultExecuting(context);
    }
}

Las opciones de configuración las proporciona el sistema de configuración mediante el patrón de opciones. Por ejemplo, desde el appsettings.json archivo :

{
  "Position": {
    "Title": "Editor",
    "Name": "Joe Smith"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

En StartUp.ConfigureServices:

  • La clase PositionOptions se agrega al contenedor de servicios con el área de configuración "Position".
  • MyActionFilterAttribute se agrega al contenedor de servicios.
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(
             Configuration.GetSection("Position"));
    services.AddScoped<MyActionFilterAttribute>();

    services.AddControllersWithViews();
}

En el código siguiente se muestra la clase PositionOptions :

public class PositionOptions
{
    public string Title { get; set; }
    public string Name { get; set; }
}

El siguiente código aplica el atributo MyActionFilterAttribute al método Index2:

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ServiceFilter(typeof(MyActionFilterAttribute))]
    public IActionResult Index2()
    {
        return Content("Header values by configuration.");
    }

En Encabezados de respuesta, author: Rick Anderson, y Editor: Joe Smith se muestra cuando se llama al Sample/Index2 punto de conexión.

El código siguiente aplica y MyActionFilterAttributeAddHeaderAttribute a la Razor página:

[AddHeader("Author", "Rick Anderson")]
[ServiceFilter(typeof(MyActionFilterAttribute))]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Los filtros no se pueden aplicar a Razor los métodos del controlador de páginas. Se pueden aplicar al Razor modelo de página o globalmente.

Algunas de las interfaces de filtro tienen atributos correspondientes que se pueden usar como clases base en las implementaciones personalizadas.

Atributos de filtro:

Ámbitos del filtro y orden de ejecución

Un filtro se puede agregar a la canalización en uno de tres ámbitos posibles:

  • Mediante un atributo en una acción del controlador. Los atributos de filtro no se pueden aplicar a Razor los métodos de controlador de Pages.
  • Usar un atributo en un controlador o Razor page.
  • Globalmente para todos los controladores, acciones y Razor Páginas, como se muestra en el código siguiente:
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter));
    });
}

Orden de ejecución predeterminado

Cuando hay varios filtros en una determinada fase de la canalización, el ámbito determina el orden predeterminado en el que esos filtros se van a ejecutar. Los filtros globales abarcan a los filtros de clase, que a su vez engloban a los filtros de método.

Como resultado de este anidamiento de filtros, el código de filtros posterior se ejecuta en el orden inverso al código anterior. La secuencia de filtro:

  • El código anterior de los filtros globales.
    • El código anterior del controlador y Razor los filtros page.
      • El código anterior de los filtros de métodos de acción.
      • El código posterior de los filtros de métodos de acción.
    • El código posterior del controlador y Razor los filtros page.
  • El código posterior de los filtros globales.

El ejemplo siguiente que ilustra el orden en el que se llama a los métodos de filtro relativos a filtros de acciones sincrónicos.

Secuencia Ámbito del filtro Método de filtro
1 Global OnActionExecuting
2 Controlador o Razor página OnActionExecuting
3 Método OnActionExecuting
4 Método OnActionExecuted
5 Controlador o Razor página OnActionExecuted
6 Global OnActionExecuted

Filtros de nivel de controlador

Cada controlador que hereda de la Controller clase base incluye Controller.OnActionExecutinglos métodos , Controller.OnActionExecutionAsyncy Controller.OnActionExecutedOnActionExecuted . Estos métodos:

  • Encapsulan los filtros que se ejecutan para una acción determinada.
  • OnActionExecuting se llama antes de cualquiera de los filtros de acciones.
  • OnActionExecuted se llama después de todos los filtros de acciones.
  • OnActionExecutionAsync se llama antes de cualquiera de los filtros de acciones. El código del filtro después de next se ejecuta después del método de acción.

Por ejemplo, en el ejemplo de descarga, se aplica MySampleActionFilter globalmente al inicio.

El TestController:

  • Aplica SampleActionFilterAttribute ([SampleActionFilter]) a la acción FilterTest2.
  • Invalida OnActionExecuting y OnActionExecuted.
public class TestController : Controller
{
    [SampleActionFilter(Order = int.MinValue)]
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

El paquete de NuGet Rick.Docs.Samples.RouteInfo proporciona MyDisplayRouteInfo y se muestra la información de ruta.

Si se dirige a https://localhost:5001/Test/FilterTest2, se ejecuta el código siguiente:

  • TestController.OnActionExecuting
    • MySampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • TestController.FilterTest2
      • SampleActionFilterAttribute.OnActionExecuted
    • MySampleActionFilter.OnActionExecuted
  • TestController.OnActionExecuted

Los filtros de nivel de controlador establecen la propiedad Order en int.MinValue. Los filtros de nivel de controlador no pueden establecerse para ejecutarse tras aplicarse los filtros a los métodos. Order se explica en la sección siguiente.

Para Razor Pages, consulte Implementación Razor de filtros de página mediante la invalidación de métodos de filtro.

Invalidación del orden predeterminado

La secuencia de ejecución predeterminada se puede invalidar con la implementación de IOrderedFilter. Order expone la propiedad IOrderedFilter que tiene prioridad sobre el ámbito a la hora de determinar el orden de ejecución. Un filtro con un valor Order menor:

  • Ejecuta el código anterior antes que el de un filtro con un valor mayor de Order.
  • Ejecuta el código posterior después que el de un filtro con un valor mayor de Order.

La propiedad Order se establece con un parámetro de constructor:

[SampleActionFilter(Order = int.MinValue)]

Tenga en cuenta los dos filtros de acción en el siguiente controlador:

[MyAction2Filter]
public class Test2Controller : Controller
{
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

Un filtro global se agrega en StartUp.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter));
    });
}

Los tres filtros se ejecutan en el siguiente orden:

  • Test2Controller.OnActionExecuting
    • MySampleActionFilter.OnActionExecuting
      • MyAction2FilterAttribute.OnActionExecuting
        • Test2Controller.FilterTest2
      • MyAction2FilterAttribute.OnResultExecuting
    • MySampleActionFilter.OnActionExecuted
  • Test2Controller.OnActionExecuted

La propiedad Order invalida el ámbito al determinar el orden en el que se ejecutarán los filtros. Los filtros se clasifican por orden en primer lugar y, después, se usa el ámbito para priorizar en caso de igualdad. Todos los filtros integrados implementan IOrderedFilter y establecen el valor predeterminado de Order en 0. Como se mencionó anteriormente, los filtros de nivel de controlador establecen la propiedad Order en int.MinValue. En el caso de los filtros integrados, el ámbito determina el orden a menos que se establezca Order en un valor distinto de cero.

En el código anterior, MySampleActionFilter tiene ámbito global, por lo que se ejecuta antes de MyAction2FilterAttribute, que tiene ámbito de controlador. Para que MyAction2FilterAttribute se ejecute en primer lugar, establezca el orden en int.MinValue:

[MyAction2Filter(int.MinValue)]
public class Test2Controller : Controller
{
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

Para que el filtro global MySampleActionFilter se ejecute en primer lugar, establezca Order en int.MinValue:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter),
                            int.MinValue);
    });
}

Cancelación y cortocircuito

La canalización de filtro se puede cortocircuitar en cualquier momento mediante el establecimiento de la propiedad Result en el parámetro ResourceExecutingContext que se ha proporcionado al método de filtro. Por ejemplo, el siguiente filtro de recursos impide que el resto de la canalización se ejecute:

public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        context.Result = new ContentResult()
        {
            Content = "Resource unavailable - header not set."
        };
    }

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
    }
}

En el siguiente código, tanto el filtro ShortCircuitingResourceFilter como el filtro AddHeader tienen como destino el método de acción SomeResource. El ShortCircuitingResourceFilter:

  • Se ejecuta en primer lugar, porque es un filtro de recursos y AddHeader es un filtro de acciones.
  • Cortocircuita el resto de la canalización.

Por tanto, el filtro AddHeader nunca se ejecuta en relación con la acción SomeResource. Este comportamiento sería el mismo si ambos filtros se aplicaran en el nivel de método de acción, siempre y cuando ShortCircuitingResourceFilter se haya ejecutado primero. ShortCircuitingResourceFilter se ejecuta primero debido a su tipo de filtro o al uso explícito de la propiedad Order.

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ServiceFilter(typeof(MyActionFilterAttribute))]
    public IActionResult Index2()
    {
        return Content("Header values by configuration.");
    }

    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header is set.");
    }

    [AddHeaderWithFactory]
    public IActionResult HeaderWithFactory()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }
}

Inserción de dependencias

Los filtros se pueden agregar por tipo o por instancia. Si se agrega una instancia, esta se utiliza para todas las solicitudes. Si se agrega un tipo, se activa por tipo. Un filtro activado por tipo significa:

Los filtros que se implementan como atributos y se agregan directamente a las clases de controlador o a los métodos de acción no pueden tener dependencias de constructor proporcionadas por la inserción de dependencias. Las dependencias de constructor no pueden proporcionarse mediante la inserción de dependencias porque:

  • Los atributos deben tener los parámetros de constructor proporcionados allá donde se apliquen.
  • Se trata de una limitación de cómo funcionan los atributos.

Los filtros siguientes admiten dependencias de constructor proporcionadas en la inserción de dependencias:

Los filtros anteriores se pueden aplicar a un método de controlador o de acción:

Los registradores están disponibles en la inserción de dependencias. Sin embargo, evite crear y utilizar filtros únicamente con fines de registro. El registro del marco integrado proporciona normalmente lo que se necesita para el registro. Registro agregado a los filtros:

  • Debe centrarse en cuestiones de dominio empresarial o en el comportamiento específico del filtro.
  • No debe registrar acciones u otros eventos del marco. Los filtros integrados registran acciones y eventos del marco.

ServiceFilterAttribute

Los tipos de implementación de filtro de servicio se registran en ConfigureServices. ServiceFilterAttribute recupera una instancia del filtro de la inserción de dependencias.

El código siguiente muestra AddHeaderResultServiceFilter:

public class AddHeaderResultServiceFilter : IResultFilter
{
    private ILogger _logger;
    public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var headerName = "OnResultExecuting";
        context.HttpContext.Response.Headers.Add(
            headerName, new string[] { "ResultExecutingSuccessfully" });
        _logger.LogInformation("Header added: {HeaderName}", headerName);
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
        _logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
    }
}

En el código siguiente, AddHeaderResultServiceFilter se agrega al contenedor de inserción de dependencias:

public void ConfigureServices(IServiceCollection services)
{
    // Add service filters.
    services.AddScoped<AddHeaderResultServiceFilter>();
    services.AddScoped<SampleActionFilterAttribute>();

    services.AddControllersWithViews(options =>
   {
       options.Filters.Add(new AddHeaderAttribute("GlobalAddHeader",
           "Result filter added to MvcOptions.Filters"));         // An instance
        options.Filters.Add(typeof(MySampleActionFilter));         // By type
        options.Filters.Add(new SampleGlobalActionFilter());       // An instance
    });
}

En el código siguiente, el atributo ServiceFilter recupera una instancia del filtro AddHeaderResultServiceFilter desde la inserción de dependencias:

[ServiceFilter(typeof(AddHeaderResultServiceFilter))]
public IActionResult Index()
{
    return View();
}

Al usar ServiceFilterAttribute, establezca ServiceFilterAttribute.IsReusable:

  • Proporciona una sugerencia que la instancia de filtro podría reutilizarse fuera del ámbito de la solicitud en la que se creó. El entorno de ejecución de ASP.NET Core no garantiza:

    • Que se creará una única instancia del filtro.
    • El filtro no volverá a solicitarse desde el contenedor de inserción de dependencias en algún momento posterior.
  • No debe usarse con un filtro que depende de servicios con una duración distinta de singleton.

ServiceFilterAttribute implementa IFilterFactory. IFilterFactory expone el método CreateInstance para crear una instancia de IFilterMetadata. CreateInstance carga el tipo especificado desde la inserción de dependencias.

TypeFilterAttribute

TypeFilterAttribute es similar a ServiceFilterAttribute, pero su tipo no se resuelve directamente desde el contenedor de inserción de dependencias, sino que crea una instancia del tipo usando el elemento Microsoft.Extensions.DependencyInjection.ObjectFactory.

Dado que los tipos TypeFilterAttribute no se resuelven directamente desde el contenedor de inserción de dependencias:

  • Los tipos a los que se hace referencia con TypeFilterAttribute no tienen que estar registrados con el contenedor de inserción de dependencias. Sus dependencias se completan a través del contenedor de inserción de dependencias.
  • TypeFilterAttribute puede aceptar opcionalmente argumentos de constructor del tipo en cuestión.

Al usar TypeFilterAttribute, establezca TypeFilterAttribute.IsReusable:

  • Proporciona una sugerencia que indica que la instancia de filtro podría reutilizarse fuera del ámbito de la solicitud en la que se creó. El entorno de ejecución de ASP.NET Core no ofrece ninguna garantía de que se vaya a crear una única instancia del filtro.

  • No debe usarse con un filtro que depende de servicios con una duración distinta de singleton.

En el siguiente ejemplo se muestra cómo pasar argumentos a un tipo mediante TypeFilterAttribute:

[TypeFilter(typeof(LogConstantFilter),
    Arguments = new object[] { "Method 'Hi' called" })]
public IActionResult Hi(string name)
{
    return Content($"Hi {name}");
}

Filtros de autorización

Filtros de autorización:

  • Son los primeros filtros que se ejecutan en la canalización del filtro.
  • Controlan el acceso a los métodos de acción.
  • Tienen un método anterior, pero no uno posterior.

Los filtros de autorización personalizados requieren un marco de autorización personalizado. Es preferible configurar directivas de autorización o escribir una directiva de autorización personalizada a escribir un filtro personalizado. El filtro de autorización integrado:

  • Llama a la autorización del sistema.
  • No autoriza las solicitudes.

No inicie excepciones dentro de los filtros de autorización:

  • La excepción no se controlará.
  • Los filtros de excepciones no controlarán la excepción.

Considere la posibilidad de emitir un desafío cuando se produzca una excepción en un filtro de autorizaciones.

Aquí encontrará más información sobre la autorización.

Filtros de recursos

Filtros de recursos:

Los filtros de recursos son útiles para cortocircuitar la mayor parte de la canalización. Por ejemplo, un filtro de almacenamiento en caché puede evitar que se ejecute el resto de la canalización en un acierto de caché.

Ejemplos de filtros de recursos:

Filtros de acciones

Los filtros de acción no se aplican a Razor Pages. Razor Pages admite IPageFilter y IAsyncPageFilter . Para obtener más información, vea Métodos de filtrado de Razor Pages.

Filtros de acciones:

El código siguiente muestra un ejemplo de filtro de acciones:

public class MySampleActionFilter : IActionFilter 
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }
}

ActionExecutingContext ofrece las siguientes propiedades:

  • ActionArguments: permite leer las entradas de un método de acción.
  • Controller: permite manipular la instancia del controlador.
  • Result: si se establece Result, se cortocircuita la ejecución del método de acción y de los filtros de acciones posteriores.

Inicio de una excepción en un método de acción:

  • Impide la ejecución de los filtros subsiguientes.
  • A diferencia del establecimiento de Result, se trata como un error en lugar de como un resultado correcto.

ActionExecutedContext proporciona Controller y Result, además de las siguientes propiedades:

  • Canceled: es true si otro filtro ha cortocircuitado la ejecución de la acción.

  • Exception: es un valor distinto de NULL si la acción o un filtro de acción de ejecución anterior han producido una excepción. Si se establece esta propiedad en un valor NULL:

    • Controla la excepción eficazmente.
    • Result se ejecuta como si se devolviera desde el método de acción.

En un IAsyncActionFilter, una llamada a ActionExecutionDelegate:

  • Ejecuta cualquier filtro de acciones posterior y el método de acción.
  • Devuelve ActionExecutedContext.

Para cortocircuitar esto, asigne Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result a una instancia de resultado y no llame a next (la clase ActionExecutionDelegate).

El marco proporciona una clase ActionFilterAttribute abstracta de la que se pueden crear subclases.

Se puede usar el filtro de acción OnActionExecuting para:

  • Validar el estado del modelo.
  • Devolver un error si el estado no es válido.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext 
                                           context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(
                                                context.ModelState);
        }
    }

Nota:

Los controladores anotados con el atributo validan automáticamente el estado del [ApiController] modelo y devuelven una respuesta 400. Para obtener más información, consulte Respuestas HTTP 400 automáticas. El método OnActionExecuted se ejecuta después del método de acción:

  • Y puede ver y manipular los resultados de la acción a través de la propiedad Result.

  • Canceled se establece en true si otro filtro ha cortocircuitado la ejecución de la acción.

  • Exception se establece en un valor distinto de NULL si la acción o un filtro de acción posterior han producido una excepción. Si Exception se establece como nulo:

    • Controla una excepción eficazmente.
    • ActionExecutedContext.Result se ejecuta como si se devolviera con normalidad desde el método de acción.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext 
                                           context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(
                                                context.ModelState);
        }
    }


    public override void OnActionExecuted(ActionExecutedContext 
                                          context)
    {
        var result = context.Result;
        // Do something with Result.
        if (context.Canceled == true)
        {
            // Action execution was short-circuited by another filter.
        }

        if(context.Exception != null)
        {
            // Exception thrown by action or action filter.
            // Set to null to handle the exception.
            context.Exception = null;
        }
        base.OnActionExecuted(context);
    }
}

Filtros de excepciones

Los filtros de excepciones:

En el siguiente filtro de excepciones de ejemplo se usa una vista de error personalizada para mostrar los detalles sobre las excepciones que se producen cuando la aplicación está en fase de desarrollo:

public class CustomExceptionFilter : IExceptionFilter
{
    private readonly IWebHostEnvironment _hostingEnvironment;
    private readonly IModelMetadataProvider _modelMetadataProvider;

    public CustomExceptionFilter(
        IWebHostEnvironment hostingEnvironment,
        IModelMetadataProvider modelMetadataProvider)
    {
        _hostingEnvironment = hostingEnvironment;
        _modelMetadataProvider = modelMetadataProvider;
    }

    public void OnException(ExceptionContext context)
    {
        if (!_hostingEnvironment.IsDevelopment())
        {
            return;
        }
        var result = new ViewResult {ViewName = "CustomError"};
        result.ViewData = new ViewDataDictionary(_modelMetadataProvider,
                                                    context.ModelState);
        result.ViewData.Add("Exception", context.Exception);
        // TODO: Pass additional detailed data via ViewData
        context.Result = result;
    }
}

El código siguiente prueba el filtro de excepciones:

[TypeFilter(typeof(CustomExceptionFilter))]
public class FailingController : Controller
{
    [AddHeader("Failing Controller", 
               "Won't appear when exception is handled")]
    public IActionResult Index()
    {
        throw new Exception("Testing custom exception filter.");
    }
}

Los filtros de excepciones:

  • No tienen eventos anteriores ni posteriores.
  • Implementan OnException o OnExceptionAsync.
  • Controle las excepciones no controladas que se producen en Razor la creación de páginas o controladores, el enlace de modelos, los filtros de acción o los métodos de acción.
  • No detectan aquellas excepciones que se produzcan en los filtros de recursos, en los filtros de resultados o en la ejecución de resultados de MVC.

Para controlar una excepción, establezca la ExceptionHandled propiedad true en o asigne la Result propiedad . Esto detiene la propagación de la excepción. Un filtro de excepciones no tiene capacidad para convertir una excepción en un proceso "correcto". Eso solo lo pueden hacer los filtros de acciones.

Los filtros de excepciones:

  • Son adecuados para interceptar las excepciones que se producen en las acciones.
  • No son tan flexibles como el middleware de control de errores.

Es preferible usar middleware de control de excepciones. Utilice los filtros de excepciones solo cuando el control de errores es diferente en función del método de acción que se llama. Por ejemplo, puede que una aplicación tenga métodos de acción tanto para los puntos de conexión de API como para las vistas/HTML. Los puntos de conexión de API podrían devolver información de error como JSON, mientras que las acciones basadas en vistas podrían devolver una página de error como HTML.

Filtros de resultados

Filtros de resultados:

IResultFilter e IAsyncResultFilter

El código siguiente muestra un filtro de resultados que agrega un encabezado HTTP:

public class AddHeaderResultServiceFilter : IResultFilter
{
    private ILogger _logger;
    public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var headerName = "OnResultExecuting";
        context.HttpContext.Response.Headers.Add(
            headerName, new string[] { "ResultExecutingSuccessfully" });
        _logger.LogInformation("Header added: {HeaderName}", headerName);
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
        _logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
    }
}

El tipo de resultado que se ejecute dependerá de la acción. Una acción que devuelve una vista incluye todo el procesamiento de Razor como parte del elemento ViewResult que se está ejecutando. Un método API puede llevar a cabo algunas funciones de serialización como parte de la ejecución del resultado. Aquí encontrará más información sobre los resultados de acciones.

Los filtros de resultados solo se ejecutan cuando una acción o un filtro de acción genera un resultado de acción. Los filtros de resultados no se ejecutan cuando:

  • Un filtro de autorización o un filtro de recursos genera un cortocircuito en la canalización.
  • Un filtro de excepciones controla una excepción al producir un resultado de acción.

El método Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting puede cortocircuitar la ejecución del resultado de la acción y de los filtros de resultados posteriores mediante el establecimiento de Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel en true. Escriba en el objeto de respuesta cuando el proceso se cortocircuite, ya que así evitará que se genere una respuesta vacía. Generación de una excepción en IResultFilter.OnResultExecuting:

  • Impide la ejecución del resultado de la acción y de los filtros subsiguientes.
  • Se trata como un error en lugar de como un resultado correcto.

Cuando se ejecuta el método Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted, es probable que la respuesta ya se haya enviado al cliente. En este caso, no se puede cambiar.

ResultExecutedContext.Canceled se establece en true si otro filtro ha cortocircuitado la ejecución del resultado de la acción.

ResultExecutedContext.Exception se establece en un valor distinto de NULL si el resultado de la acción o un filtro de resultado posterior ha producido una excepción. Establecer Exception en un valor null hace que una excepción se controle de forma eficaz y evita que se vuelva a producir dicha excepción más adelante en la canalización. No hay una forma confiable de escribir datos en una respuesta cuando se controla una excepción en un filtro de resultados. Si los encabezados ya se han vaciado en el cliente si el resultado de una acción inicia una excepción, no hay ningún mecanismo confiable que permita enviar un código de error.

En un elemento IAsyncResultFilter, una llamada a await next en ResultExecutionDelegate ejecuta cualquier filtro de resultados posterior y el resultado de la acción. Para cortocircuitar, establezca ResultExecutingContext.Cancel en true y no llame a ResultExecutionDelegate:

public class MyAsyncResponseFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(ResultExecutingContext context,
                                             ResultExecutionDelegate next)
    {
        if (!(context.Result is EmptyResult))
        {
            await next();
        }
        else
        {
            context.Cancel = true;
        }

    }
}

El marco proporciona una clase ResultFilterAttribute abstracta de la que se pueden crear subclases. La clase AddHeaderAttribute mostrada anteriormente es un ejemplo de un atributo de filtro de resultados.

IAlwaysRunResultFilter e IAsyncAlwaysRunResultFilter

Las interfaces IAlwaysRunResultFilter e IAsyncAlwaysRunResultFilter declaran una implementación IResultFilter que se ejecuta para obtener todos los resultados de la acción. Esto incluye los resultados de la acción generados por:

  • Filtros de autorización y filtros de recursos que generan un cortocircuito.
  • Filtros de excepción.

Por ejemplo, el siguiente filtro siempre ejecuta y establece un resultado de la acción (ObjectResult) con un código de estado 422 - Entidad no procesable cuando se produce un error en la negociación de contenido:

public class UnprocessableResultFilter : Attribute, IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is StatusCodeResult statusCodeResult &&
            statusCodeResult.StatusCode == (int) HttpStatusCode.UnsupportedMediaType)
        {
            context.Result = new ObjectResult("Can't process this!")
            {
                StatusCode = (int) HttpStatusCode.UnsupportedMediaType,
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
    }
}

IFilterFactory

IFilterFactory implementa IFilterMetadata. Por tanto, una instancia de IFilterFactory se puede usar como una instancia de IFilterMetadata en cualquier parte de la canalización de filtro. Cuando el entorno de ejecución se prepara para invocar el filtro, intenta convertirlo a un IFilterFactory. Si esa conversión se realiza correctamente, se llama al método CreateInstance para crear la instancia IFilterMetadata que se va a invocar. Esto proporciona un diseño flexible, dado que no hay que establecer la canalización de filtro exacta de forma explícita cuando la aplicación se inicia.

IFilterFactory.IsReusable:

  • Es una sugerencia del generador en la que se puede reutilizar la instancia de filtro creada por el generador fuera del ámbito de solicitud en el que se creó.
  • No se debe usar con un filtro que dependa de los servicios con una duración distinta de singleton.

El entorno de ejecución de ASP.NET Core no garantiza:

  • Que se creará una única instancia del filtro.
  • El filtro no volverá a solicitarse desde el contenedor de inserción de dependencias en algún momento posterior.

Advertencia

IFilterFactory.IsReusable Configure solo para devolver true si el origen de los filtros no es ambiguo, los filtros no tienen estado y los filtros son seguros de usar en varias solicitudes HTTP. Por ejemplo, no devuelva filtros de di que están registrados como de ámbito o transitorios si IFilterFactory.IsReusable devuelve true. Puede implementar IFilterFactory con las implementaciones de atributos personalizados como método alternativo para crear filtros:

public class AddHeaderWithFactoryAttribute : Attribute, IFilterFactory
{
    // Implement IFilterFactory
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return new InternalAddHeaderFilter();
    }

    private class InternalAddHeaderFilter : IResultFilter
    {
        public void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                "Internal", new string[] { "My header" });
        }

        public void OnResultExecuted(ResultExecutedContext context)
        {
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

El filtro se aplica en el código siguiente:

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ServiceFilter(typeof(MyActionFilterAttribute))]
    public IActionResult Index2()
    {
        return Content("Header values by configuration.");
    }

    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header is set.");
    }

    [AddHeaderWithFactory]
    public IActionResult HeaderWithFactory()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }
}

Pruebe el código anterior mediante la ejecución del ejemplo de descarga:

  • Invoque las herramientas de desarrollador de F12.
  • Vaya a https://localhost:5001/Sample/HeaderWithFactory.

Las herramientas de desarrollador F12 muestran los siguientes encabezados de respuesta agregados por el código de ejemplo:

  • Autor:Rick Anderson
  • globaladdheader:Result filter added to MvcOptions.Filters
  • Interna:My header

El código anterior crea el encabezado de respuesta interno:My header .

IFilterFactory implementado en un atributo

Los filtros que implementan IFilterFactory son útiles para los filtros que:

  • No requieren pasar parámetros.
  • Tienen dependencias de constructor que deben completarse por medio de la inserción de dependencias.

TypeFilterAttribute implementa IFilterFactory. IFilterFactory expone el método CreateInstance para crear una instancia de IFilterMetadata. CreateInstance carga el tipo especificado desde el contenedor de servicios (inserción de dependencias).

public class SampleActionFilterAttribute : TypeFilterAttribute
{
    public SampleActionFilterAttribute()
                         :base(typeof(SampleActionFilterImpl))
    { 
    }

    private class SampleActionFilterImpl : IActionFilter
    {
        private readonly ILogger _logger;
        public SampleActionFilterImpl(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<SampleActionFilterAttribute>();
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
           _logger.LogInformation("SampleActionFilterAttribute.OnActionExecuting");
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _logger.LogInformation("SampleActionFilterAttribute.OnActionExecuted");
        }
    }
}

El código siguiente muestra tres métodos para aplicar [SampleActionFilter]:

[SampleActionFilter]
public IActionResult FilterTest()
{
    return Content("From FilterTest");
}

[TypeFilter(typeof(SampleActionFilterAttribute))]
public IActionResult TypeFilterTest()
{
    return Content("From TypeFilterTest");
}

// ServiceFilter must be registered in ConfigureServices or
// System.InvalidOperationException: No service for type '<filter>'
// has been registered. Is thrown.
[ServiceFilter(typeof(SampleActionFilterAttribute))]
public IActionResult ServiceFilterTest()
{
    return Content("From ServiceFilterTest");
}

En el código anterior, decorar el método con [SampleActionFilter] es el enfoque preferido para aplicar SampleActionFilter.

Uso de middleware en la canalización de filtro

Los filtros de recursos funcionan como el middleware, en el sentido de que se encargan de la ejecución de todo lo que viene después en la canalización. Pero los filtros se diferencian del middleware en que forman parte del runtime, lo que significa que tienen acceso al contexto y las construcciones.

Para usar middleware como un filtro, cree un tipo con un método Configure en el que se especifique el middleware para insertar en la canalización de filtro. El ejemplo siguiente usa el middleware de localización para establecer la referencia cultural actual de una solicitud:

public class LocalizationPipeline
{
    public void Configure(IApplicationBuilder applicationBuilder)
    {
        var supportedCultures = new[]
        {
            new CultureInfo("en-US"),
            new CultureInfo("fr")
        };

        var options = new RequestLocalizationOptions
        {
            DefaultRequestCulture = new RequestCulture(
                                       culture: "en-US", 
                                       uiCulture: "en-US"),
            SupportedCultures = supportedCultures,
            SupportedUICultures = supportedCultures
        };
        options.RequestCultureProviders = new[] 
            { new RouteDataRequestCultureProvider() {
                Options = options } };

        applicationBuilder.UseRequestLocalization(options);
    }
}

Use MiddlewareFilterAttribute para ejecutar el middleware:

[Route("{culture}/[controller]/[action]")]
[MiddlewareFilter(typeof(LocalizationPipeline))]
public IActionResult CultureFromRouteData()
{
    return Content(
          $"CurrentCulture:{CultureInfo.CurrentCulture.Name},"
        + $"CurrentUICulture:{CultureInfo.CurrentUICulture.Name}");
}

Los filtros de middleware se ejecutan en la misma fase de la canalización de filtro que los filtros de recursos, antes del enlace de modelos y después del resto de la canalización.

Seguridad para subprocesos

Al pasar una instancia de un filtro a Add, en lugar de a su Type, el filtro es un singleton y no es seguro para subprocesos.

Siguientes acciones