Middlewares and Filters in .NET

In this post we’re going to see what middlewares are in .NET as well as filters (FilterAttribute), we'll go through their differences and how to implement them.

 

 

1 - What is a middleware in .NET?

To understand the differences between middleware and filters, we must first understand what each one is.

First, let me say that I personally dislike the term “middleware” because it doesn't clearly represent where it is located. I mean, yes, it’s in the middle. But personally, it doesn’t mean much to me.

 

In .NET, a middleware is a class that allows you to manipulate an HTTP request (or response).

 

A middleware acts on every request that reaches our application, so it's part of the request pipeline.

And they also act on the response, but in reverse order.

request pipeline

Since the arrival of .NET Core, the use of middleware has become very common, so it’s very important to understand the logic behind it and how it works.

It’s worth noting that the order is important, as I mentioned, they are executed one after another.

 

You should keep in mind that we can create a middleware that, under certain conditions, generates a response directly. This means that any middleware that comes after it will not be executed.

 

 

1.1 - Examples of middleware in ASP.NET Core

We have examples of middlewares: when you create an API using the Visual Studio template, it includes the statement app.UseHttpsRedirection(); which, behind the scenes, adds a middleware that redirects all HTTP requests to HTTPS.

CORS or routing are other very common features, which are also implemented via middlewares.

 

Or, for instance, if we have app.UseStaticFiles() enabled (usually as the first middleware), and if the request is a “get file” such as a CSS or JavaScript, we will detect that the file is in the static files folder and return it, without executing the rest of the middlewares.

response from middleware

 

1.2 - Creating a middleware in .NET

To create a middleware in .NET you just need to make a normal class, but with two rules:

  1. The constructor must receive a RequestDelegate.
  2. A method named Invoke that takes HttpContext as parameter.
public class EjemploMiddleware{    private readonly RequestDelegate _next;    public EjemploMiddleware(RequestDelegate next)    {        _next = next;    }    public async Task Invoke(HttpContext context)    {      await _next(context);    }}

And inside the class we can do whatever we want. We can even access the dependency container through context.RequestServices.GetService<T>() or use regular dependency injection via the constructor.

 

Now let's add some logic; in the example, it's simple: logging the time the request takes, but we can make it as complex as we want.

public class EjemploMiddleware{    private readonly RequestDelegate _next;    private readonly ILogger _logger;    public EjemploMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)    {        _next = next;        _logger = loggerFactory.CreateLogger(typeof(EjemploMiddleware));    }    public async Task Invoke(HttpContext context)    {        //This ID is to simulate an ID coming from the FrontEnd/Container        //With which we keep "trace" of the whole user action        //Before the request        Guid traceId = Guid.NewGuid();        _logger.LogDebug($"Request {traceId} started");        Stopwatch stopWatch = new Stopwatch();        stopWatch.Start();        await _next(context);        //After the request        stopWatch.Stop();        TimeSpan ts = stopWatch.Elapsed;        string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",        ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);        _logger.LogDebug($"Request {traceId} took {elapsedTime} ");    }}

To activate our middleware, we need to do it in the Configure method inside startup.cs, and to do that we just include the following code.

app.UseMiddleware<EjemploMiddleware>();
  • Note: In Blazor WA, we can’t define middlewares because there is no pipeline.

 

And with that, we have our middleware enabled and running. If we make a request, we can see the result:

middleware .net result

 

 

2 - What is a filter in .NET?

First of all, when we say filter, we’re referring to an ActionFilterAttribute.

The behavior is similar to middleware, but in this case, we will use them when we want more control or to be more precise with what we can do.

 

While in middleware we only have access to HttpContext, in the ActionFilterAttribute we have access to much more information, such as the model and of course also HttpContext.

filterattribute

If we were to show in an image the pipeline of middlewares and the pipeline of filters, the filters would go after the middlewares. But unlike middlewares, with filters, we can specify exactly where we want to use them.

That’s why we say they are more specific, because we may want an endpoint to have a filter or not:

middleware and filters in the .net pipeline

As we can see in the image, filter 2 is common to both endpoints, while filter 1 and filter 3 are exclusive to one of the endpoints.

  • Note: ActionFilterAttribute is not the only filter in .NET, for example, ExceptionFilter, which is triggered when there is an exception.

 

 

2.1 - Examples of filters in ASP.NET

The most common examples are Authorize, which is an action filter that allows us to restrict access to an endpoint depending on the user or their role.

  • Note: I’ll create a post about it soon.

Another very common example is OutPutCache, which keeps the endpoint’s response in cache for as long as we specify.

 

Of course, the filter pipeline works just like the middleware pipeline—we can return the response from one of them. The most common case is Authorize. With the following directive [Authorize(Policy = "PuedeConsultarUsuarios")], the logged-in user needs that policy to access the endpoint.

 

 

2.3 Implementing a filter in ASP.NET

To create a filter is very simple: first, you need to create a class that implements any of the filters that .NET provides, in our case ActionFilterAttribute.

 

Just like with middleware, we have access to dependency injection from the constructor.

 

Then we override the methods we need, OnActionExecuting will be executed before the endpoint, and OnActionExecuted after.

Here you can see an example:

public class EjemploFiltro : ActionFilterAttribute{    private readonly ILogger<EjemploFiltro> _logger;    public EjemploFiltro(ILogger<EjemploFiltro> logger)    {        _logger = logger;    }    public override void OnActionExecuting(ActionExecutingContext context)    {        _logger.LogInformation($"before the method {context.ActionDescriptor.DisplayName}");        base.OnActionExecuting(context);    }    public override void OnActionExecuted(ActionExecutedContext context)    {        _logger.LogInformation($"after the method {context.ActionDescriptor.DisplayName}");        base.OnActionExecuted(context);    }}

In the example, it just logs the method that we are executing.

 

 

2.4 - Implementing an Async filter in ASP.NET

If we're working with an asynchronous method, we need to use IAsyncActionFilter, overriding the OnActionExecutionAsync method.

public class EjemploFiltro : IAsyncActionFilter{    private readonly ILogger<EjemploFiltro> _logger;    public EjemploFiltro(ILogger<EjemploFiltro> logger)    {        _logger = logger;    }    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)    {        _logger.LogInformation($"Before the method {context.ActionDescriptor.DisplayName}");        await next();        _logger.LogInformation($"After the method {context.ActionDescriptor.DisplayName}");    }}

As we can see, it looks very similar to middleware.

 

Now, we need to add a bit of configuration to use it. First, add it to the dependency container:

services.AddScoped<EjemploFiltro>();
  • Note: We need to inject it because we are injecting the logger.

Then on the method/endpoint we want to use it, we indicate it with [ServiceFilter(typeof(EjemploFiltro))], like this:

[ServiceFilter(typeof(EjemploFiltro))][HttpGet("{userName}")]public async Task<ResultDto<PersonalProfileDto>> Get(string userName){    return (await GetProfile(userName)).MapDto(x=>x);}
  • Note: if we weren’t injecting the logger, we could just call the filter directly with `[EjemploFiltro]`

 

 

Conclusion

In this post we've covered:

  • What a middleware is and how to create one in .NET
  • What a Filter attribute is and how to create one in .NET
  • How the request pipeline in .NET works
  • Code examples for Middlewares in .NET
  • Code examples for filters in .NET

 

This post was translated from Spanish. You can see the original one here.
If there is any problem you can add a comment bellow or contact me in the website's contact form

© copyright 2025 NetMentor | Todos los derechos reservados | RSS Feed

Buy me a coffee Invitame a un café