Filtros en ASP.NET Core
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, que impide el acceso a los recursos para los que un usuario no está autorizado.
- Almacenamiento en caché de respuestas, cortocircuando 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 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 el 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.

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.
-
- Se ejecutan después de la autorización.
- OnResourceExecuting ejecuta código antes que el resto de la canalización del filtro. Por ejemplo,
OnResourceExecutingejecuta 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.
-
- 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.

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:
- Sincrónicas: IActionFilter y IResultFilter
- Asincrónicas: IAsyncActionFilter y IAsyncResultFilter
- IOrderedFilter
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
PositionOptionsse agrega al contenedor de servicios con el área de configuración"Position". MyActionFilterAttributese 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 se muestra cuando se llama al punto de Editor: Joe Smith Sample/Index2 conexión.
El código siguiente aplica y MyActionFilterAttribute a AddHeaderAttribute 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 page. Se pueden aplicar al modelo Razor 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:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
Á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 una página.
- 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 los filtros de Razor página.
- 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.
- Código posterior del controlador y filtros Razor de página.
- El código anterior del controlador y los filtros de Razor página.
- 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 los Controller.OnActionExecuting métodos , y Controller.OnActionExecutionAsync Controller.OnActionExecuted
OnActionExecuted . Estos métodos:
- Encapsulan los filtros que se ejecutan para una acción determinada.
OnActionExecutingse llama antes de cualquiera de los filtros de acciones.OnActionExecutedse llama después de todos los filtros de acciones.OnActionExecutionAsyncse llama antes de cualquiera de los filtros de acciones. El código del filtro después denextse 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ónFilterTest2. - Invalida
OnActionExecutingyOnActionExecuted.
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.OnActionExecutingMySampleActionFilter.OnActionExecutingSampleActionFilterAttribute.OnActionExecutingTestController.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, vea Implementar filtros de Razor página reemplazando 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.OnActionExecutingMySampleActionFilter.OnActionExecutingMyAction2FilterAttribute.OnActionExecutingTest2Controller.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
AddHeaderes 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:
- Se crea una instancia para cada solicitud.
- Las dependencias de constructor se rellenan mediante la inserción de dependencias.
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:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory se implementa en el atributo.
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();
}
Cuando se usa ServiceFilterAttribute , se establece 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
TypeFilterAttributeno 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. TypeFilterAttributepuede aceptar opcionalmente argumentos de constructor del tipo en cuestión.
Cuando se usa TypeFilterAttribute , se establece 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:
- Implementan la interfaz IResourceFilter o IAsyncResourceFilter.
- La ejecución encapsula la mayor parte de la canalización de filtro.
- Los filtros de autorizaciones son los únicos que se ejecutan antes que los 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:
El filtro de recursos de cortocircuito mostrado anteriormente.
DisableFormValueModelBindingAttribute:
- Evita que el enlace de modelos tenga acceso a los datos del formulario.
- Se utiliza cuando hay cargas de archivos muy voluminosos para impedir que los datos del formulario se lean en la memoria.
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:
- Implementan la interfaz IActionFilter o IAsyncActionFilter.
- Su ejecución rodea la ejecución de los métodos de acción.
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.
Resultse 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
Exceptionse establece como nulo:- Controla una excepción eficazmente.
ActionExecutedContext.Resultse 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:
- Implementan IExceptionFilter o IAsyncExceptionFilter.
- Se pueden usar para implementar directivas de control de errores comunes.
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 la creación de páginas o controladores, el enlace de modelos, los filtros Razor 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 propiedad ExceptionHandled en true o escriba una respuesta. 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 sobre errores 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:
- Implementar una interfaz:
- Su ejecución rodea la ejecución de los resultados de acción.
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 cortocircuición, ResultExecutingContext.Cancel establezca en y no llame a true 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 de que la instancia de filtro creada por el generador se puede reutilizar fuera del ámbito de solicitud en el que se creó.
- No debe usarse con un filtro que dependa de los servicios con una duración que no sea 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
Configure solo para devolver si el origen de los filtros no es ambiguo, los filtros no tienen estado y los filtros son seguros para su uso en varias IFilterFactory.IsReusable true solicitudes HTTP. Por ejemplo, no devuelva filtros de la di di de que están registrados como con á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:
- author:
Rick Anderson - globaladdheader:
Result filter added to MvcOptions.Filters - internal:
My header
El código anterior crea el encabezado de respuesta internal: 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 , en lugar de , el filtro es Add un Type singleton y no es seguro para subprocesos.
Siguientes acciones
- Vea Métodos de filtro para Razor Pages.
- Para experimentar con los filtros, descargue, pruebe y modifique el ejemplo de GitHub.
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 (impedir el acceso a los recursos a un usuario que no está autorizado).
- Almacenamiento en caché de respuestas (cortocircuitar la canalización de solicitud 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.
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.

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.
-
- Se ejecutan después de la autorización.
- OnResourceExecuting puede ejecutar código antes que el resto de la canalización del filtro. Por ejemplo,
OnResourceExecutingpuede ejecutar código antes que el enlace de modelos. - OnResourceExecuted puede ejecutar el código una vez que el resto de la canalización se haya completado.
Los filtros de acciones pueden ejecutar código inmediatamente antes y después de llamar a un método de acción individual. Se pueden usar para manipular los argumentos pasados a una acción y el resultado obtenido de la acción. Los filtros de acción no se admiten en Razor Pages.
Los filtros de excepciones sirven para aplicar directivas globales a las excepciones no controladas que se producen antes de que se escriba algo en el cuerpo de respuesta.
Los filtros de resultados pueden ejecutar código inmediatamente antes y después de la ejecución de resultados de acción individuales. 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.

Implementación
Los filtros admiten implementaciones tanto sincrónicas como asincrónicas a través de diferentes definiciones de interfaz.
Los filtros sincrónicos pueden ejecutar código antes (On-Stage-Executing) y después (On-Stage-Executed) 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.
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
}
}
Los filtros asincrónicos definen un método On-Stage-ExecutionAsync:
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. Cada uno de los métodos On-Stage-ExecutionAsync toman un FilterType-ExecutionDelegate que ejecuta la fase de canalización del filtro.
Varias fases de filtro
Se pueden implementar interfaces para varias fases de filtro en una sola clase. Por ejemplo, la clase ActionFilterAttribute implementa IActionFilter, IResultFilter y sus equivalentes asincrónicos.
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, se invalidan solo los métodos sincrónicos o el método asincrónico de 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", "Joe Smith")]
public class SampleController : Controller
{
public IActionResult Index()
{
return Content("Examine the headers using the F12 developer tools.");
}
[ShortCircuitingResourceFilter]
public IActionResult SomeResource()
{
return Content("Successful access to resource - header is set.");
}
Algunas de las interfaces de filtro tienen atributos correspondientes que se pueden usar como clases base en las implementaciones personalizadas.
Atributos de filtro:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
Á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.
- Mediante un atributo en un controlador.
- Globalmente para todos los controladores y las acciones, tal como se muestra en el siguiente código:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(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
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
El código anterior agrega tres filtros globalmente mediante la colección MvcOptions.Filters.
Orden de ejecución predeterminado
Cuando hay varios filtros del mismo tipo, el ámbito determina el orden predeterminado en el que esos filtros se van a ejecutar. Los filtros globales delimitan los filtros de clase. Los filtros de clase delimitan 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 anterior de los filtros de controlador.
- 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 | Controller | OnActionExecuting |
| 3 | Método | OnActionExecuting |
| 4 | Método | OnActionExecuted |
| 5 | Controller | OnActionExecuted |
| 6 | Global | OnActionExecuted |
Esta secuencia pone de manifiesto lo siguiente:
- El filtro de método está anidado en el filtro de controlador.
- El filtro de controlador está anidado en el filtro global.
Filtros de nivel Razor de página y controlador
Cada controlador que hereda de la Controller clase base incluye los Controller.OnActionExecuting métodos , y Controller.OnActionExecutionAsync Controller.OnActionExecuted
OnActionExecuted . Estos métodos:
- Encapsulan los filtros que se ejecutan para una acción determinada.
OnActionExecutingse llama antes de cualquiera de los filtros de acciones.OnActionExecutedse llama después de todos los filtros de acciones.OnActionExecutionAsyncse llama antes de cualquiera de los filtros de acciones. El código del filtro después denextse 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ónFilterTest2. - Invalida
OnActionExecutingyOnActionExecuted.
public class TestController : Controller
{
[SampleActionFilter]
public IActionResult FilterTest2()
{
return Content($"From FilterTest2");
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
base.OnActionExecuted(context);
}
}
Si se dirige a https://localhost:5001/Test/FilterTest2, se ejecuta el código siguiente:
TestController.OnActionExecutingMySampleActionFilter.OnActionExecutingSampleActionFilterAttribute.OnActionExecutingTestController.FilterTest2
SampleActionFilterAttribute.OnActionExecuted
MySampleActionFilter.OnActionExecuted
TestController.OnActionExecuted
Para Razor Pages, vea Implementar filtros de Razor página invalidando los 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 puede establecer con un parámetro de constructor:
[MyFilter(Name = "Controller Level Attribute", Order=1)]
Considere los mismos tres filtros de acción que se muestran en el ejemplo anterior. Si la propiedad Order del controlador y de los filtros globales está establecida en 1 y 2 respectivamente, el orden de ejecución se invierte.
| Secuencia | Ámbito del filtro | PropiedadOrder |
Método de filtro |
|---|---|---|---|
| 1 | Método | 0 | OnActionExecuting |
| 2 | Controller | 1 | OnActionExecuting |
| 3 | Global | 2 | OnActionExecuting |
| 4 | Global | 2 | OnActionExecuted |
| 5 | Controller | 1 | OnActionExecuted |
| 6 | Método | 0 | 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. En los filtros integrados, el ámbito determina el orden, a menos que Order se establezca en un valor distinto de cero.
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
AddHeaderes 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", "Joe Smith")]
public class SampleController : Controller
{
public IActionResult Index()
{
return Content("Examine the headers using the F12 developer tools.");
}
[ShortCircuitingResourceFilter]
public IActionResult SomeResource()
{
return Content("Successful access to resource - header is set.");
}
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:
- Se crea una instancia para cada solicitud.
- Las dependencias de constructor se rellenan mediante la inserción de dependencias.
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:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory se implementa en el atributo.
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.
}
}
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.AddMvc(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
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
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();
}
Cuando se usa ServiceFilterAttribute , se establece 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
TypeFilterAttributeno 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. TypeFilterAttributepuede aceptar opcionalmente argumentos de constructor del tipo en cuestión.
Cuando se usa TypeFilterAttribute , se establece 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}");
}
public class LogConstantFilter : IActionFilter
{
private readonly string _value;
private readonly ILogger<LogConstantFilter> _logger;
public LogConstantFilter(string value, ILogger<LogConstantFilter> logger)
{
_logger = logger;
_value = value;
}
public void OnActionExecuting(ActionExecutingContext context)
{
_logger.LogInformation(_value);
}
public void OnActionExecuted(ActionExecutedContext context)
{ }
}
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:
- Implementan la interfaz IResourceFilter o IAsyncResourceFilter.
- La ejecución encapsula la mayor parte de la canalización de filtro.
- Los filtros de autorizaciones son los únicos que se ejecutan antes que los 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:
El filtro de recursos de cortocircuito mostrado anteriormente.
DisableFormValueModelBindingAttribute:
- Evita que el enlace de modelos tenga acceso a los datos del formulario.
- Se utiliza cuando hay cargas de archivos muy voluminosos para impedir que los datos del formulario se lean en la memoria.
Filtros de acciones
Importante
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:
- Implementan la interfaz IActionFilter o IAsyncActionFilter.
- Su ejecución rodea la ejecución de los métodos de acción.
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.
}
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.
Resultse 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);
}
}
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
Exceptionse establece como nulo:- Controla una excepción eficazmente.
ActionExecutedContext.Resultse 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:
- Implementan IExceptionFilter o IAsyncExceptionFilter.
- Se pueden usar para implementar directivas de control de errores comunes.
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 IHostingEnvironment _hostingEnvironment;
private readonly IModelMetadataProvider _modelMetadataProvider;
public CustomExceptionFilter(
IHostingEnvironment 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;
}
}
Los filtros de excepciones:
- No tienen eventos anteriores ni posteriores.
- Implementan OnException o OnExceptionAsync.
- Controle las excepciones no controladas que se producen en la creación de páginas o controladores, el enlace de modelos, los filtros de Razor 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 propiedad ExceptionHandled en true o escriba una respuesta. 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 sobre errores 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:
- Implementar una interfaz:
- Su ejecución rodea la ejecución de los resultados de acción.
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.
}
}
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. Si se inicia una excepción en IResultFilter.OnResultExecuting, sucederá lo siguiente:
- Se impedirá la ejecución del resultado de la acción y de los filtros subsiguientes.
- Se tratará 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 más.
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 ASP.NET Core 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 cortocircuición, ResultExecutingContext.Cancel establezca en y no llame a true 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.
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 código anterior se puede probar mediante la ejecución del ejemplo de descargar:
- 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:
- author:
Joe Smith - globaladdheader:
Result filter added to MvcOptions.Filters - internal:
My header
El código anterior crea el encabezado de respuesta internal: 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("Business action starting...");
// perform some business logic work
}
public void OnActionExecuted(ActionExecutedContext context)
{
// perform some business logic work
_logger.LogInformation("Business action completed.");
}
}
}
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 ServiceFilterTest");
}
// 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 entorno de ejecución de ASP.NET Core, lo que significa que tienen acceso al contexto y las construcciones de ASP.NET Core.
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 , en lugar de , el filtro es Add un Type singleton y no es seguro para subprocesos.
Siguientes acciones
- Vea Métodos de filtro para Razor Pages.
- Para experimentar con los filtros, descargue, pruebe y modifique el ejemplo de GitHub.