Introduction
Filters are essential concepts of ASP.NET Core. Although Filters were present in Legacy ASP.Net Framework, middleware was introduced in ASP.Net Core. In Legacy ASP.NET Framework we had HttpModules and HttpHandlers which were part of the request pipeline but in ASP.NET Core we have middleware which is part of the request pipeline. Both Filters and Middlewares have some common usage like logging the request or response, authenticating the request and error handling for all the requests, etc...
In this article, we will know about filters only and check Middleware in a different article.
Filters
- Authorization Filter: This filter executes first which checks whether the user is authorized for the request and returns HTTP Status code 401 if the user is unauthorized for the request.
- Resource Filters: This Filter executes after the Authorization Filter and is generally used for caching implementations. OnResourceExecuting and OnResourceExecuted are the two methods for Resource Filter where the former hits before model binding whereas later executes code after the rest of the pipeline is completed.
- Action Filters: Used for executing some logic before and after Action Method executes in ASP.Net Core. The synchronous version contains two important methods namely OnActionExecuting and OnActionExecuted where we can execute some business logic before and after the request hit the Action method whereas the asynchronous version provides a single method named OnActionExecutionAsync for executing logic before and after the action method. Common usage of this Action Filter is Logging the request and response, Validating the incoming request for some specific token in the request header, etc...
- Exception Filter: This Filter is used to handle exceptions in the application globally. This filter is useful when we don't want the try/catch block in individual methods and want to handle the exceptions globally. It can also be used in ASP.NET Core Web Api where we want to handle exceptions globally and don't want to show the server-side exception details in UI and instead send customized error messages to UI. Check this link Global Exception Filter to find an example of the same.
- Result Filter: this Filter executes last in the request pipeline and has two important methods namely OnResultExecuting and OnResultExecuted which are executed before and after the result from the action method is processed.
public class ActionFilterExample : IActionFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ActionFilterExample(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
}
public void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
var token = _httpContextAccessor.HttpContext.Request.Headers["token"].ToString();
if (string.IsNullOrEmpty(token))
{
context.HttpContext.Response.StatusCode = 401; // BadRequest
context.HttpContext.Response.WriteAsync("Token Missing");
return;
}
if(!ValidateToken(token))
{
context.HttpContext.Response.StatusCode = 401; // BadRequest
context.HttpContext.Response.WriteAsync("Token Missing");
return;
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
throw new System.NotImplementedException();
}
}
The same token can be validated in Asynchronous Filter as well like below
public class AsyncActionFilterExample : IAsyncActionFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
public AsyncActionFilterExample(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// Do something before the action executes.
var token = _httpContextAccessor.HttpContext.Request.Headers["token"].ToString();
if (string.IsNullOrEmpty(token))
{
context.HttpContext.Response.StatusCode = 401; // BadRequest
await context.HttpContext.Response.WriteAsync("Token Missing");
return;
}
if (!ValidateToken(token))
{
context.HttpContext.Response.StatusCode = 401; // BadRequest
await context.HttpContext.Response.WriteAsync("Token Missing");
return;
}
// next() calls the action method.
var resultContext = await next();
// resultContext.Result is set.
// Do something after the action executes.
}
}
As you can see in the asynchronous version we have one method and are using ActionExecutionDelegate next, which runs the code of Action Methods.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<ActionFilterExample>();
}
Registering your filter in IoC Container is important otherwise we will get InvalidOperationException.
Now we can use the above Filter either at the entire controller level or individual Action method level.
Controller Level
[Route("api/[controller]")]
[ApiController]
[TypeFilter(typeof(ActionFilterExample))]
public class HomeController: ControllerBase
{
}
Action Method Level
[Route("api/[controller]")]
[ApiController]
public class HomeController: ControllerBase
{
[TypeFilter(typeof(ActionFilterExample))]
public ActionResult Get(int id)
{
return Ok();
}
public ActionResult Post(int id)
{
return Ok();
}
}
public class ResultFilterExample : ActionFilterAttribute
{
private readonly string _name;
private readonly string _value;
public ResultFilterExample(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);
}
}
The above Filter can be used in the Controller like below
[Route("api/[controller]")]
[ApiController]
public class HomeController: ControllerBase
{
[ResultFilterExample("Filter-Header", "Filter Value")]
public IActionResult Index() =>
Content("Examine the response headers using the F12 developer tools.");
}
Filter Default Order of Execution
The filter sequence is mentioned in the Official Microsoft Link:
- The before code of global filters.
- The before code of controller filters.
- The before code of action method filters.
- The after code of action method filters.
- The after code of controller filters.
- The before code of controller filters.
- The after code of global filters.
Sequence | Filter scope | Filter method |
---|---|---|
1 | Global | OnActionExecuting |
2 | Controller | OnActionExecuting |
3 | Action | OnActionExecuting |
4 | Action | OnActionExecuted |
5 | Controller | OnActionExecuted |
6 | Global | OnActionExecuted |
Share This Post
Support Me