Home About Lessons Blog On The Mic Contact Projects
Lessons ASP.NET CORE · C#

ASP.NET Core — Filter vs Middleware

9 min read · Emre Ulutabak
1
The big picture

In an ASP.NET Core application, a request does not jump directly into the controller. It first passes through the request pipeline, and only then reaches the controller and action if allowed.

That is why some checks belong to the outer layer, while others belong closer to the action. This is exactly where the difference between middleware and filters begins.

text
Browser -> Middleware -> Controller -> Action -> Response
💡
Middleware lives in the outer layer. A filter works closer to the action inside MVC.
2
What is middleware?

Middleware is the general control layer that almost every incoming request passes through. The request is handled here before it ever reaches the controller.

Authentication, authorization, logging, and global exception handling are typical responsibilities of this layer.

In short, middleware asks: Can this request continue through the system?

3
What is a filter?

A filter works on the MVC side, much closer to the controller and action. It does not run for every request, only when the related controller or action is involved.

That makes it a strong fit for checks that are close to business rules, such as active-plan validation, feature access, or action-specific restrictions.

A filter usually asks: Can this user execute this action?

4
Story: The banned user

Imagine a software system where an admin bans a user from the admin panel. You no longer want that person moving around inside the application.

Even if the user is already logged in, the system should notice it on the next refresh or request. This should be enforced across the whole application, not just inside a specific controller.

This scenario smells much more like middleware than a filter. The goal is not to block one action, but to stop the user from continuing through the system at all.

5
Why middleware here?

Because a ban check is a system-wide security decision. You do not want the user blocked in one place but still free to move elsewhere. If the user is banned, that fact should be considered on every new request.

Middleware fits this well because it runs before the controller. If the user is banned, the session can be closed, access can be denied, and the user can be redirected appropriately.

So middleware here is not asking “Can this user access this action?” but rather “Can this user remain in the system at all?”

💡
Global security and system-level access decisions usually belong more naturally in middleware.
6
When is a filter the better choice?

Not every check should become middleware. For example, whether a user has an active plan, can access a specific product, or is allowed into a premium-only action are checks much closer to the action level.

These should not run for every request, only when the related controller or action is being used. That is where a filter becomes the cleaner and more precise solution.

So system-level decisions like bans often belong in middleware, while business-rule checks usually fit filters better.

7
A short middleware example

In the example below, the middleware checks the ban status of an authenticated user. If the user is banned, the system signs them out and redirects them to a dedicated page.

This means the user is pushed out of the system even on the very next refresh.

csharp
public class BanCheckMiddleware
{
    private readonly RequestDelegate _next;

    public BanCheckMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var user = context.User;

        if (user.Identity != null && user.Identity.IsAuthenticated)
        {
            var isBanned = false;

            if (isBanned)
            {
                await context.SignOutAsync();
                context.Response.Redirect("/Account/Banned");
                return;
            }
        }

        await _next(context);
    }
}
💡
Middleware can stop the flow early, preventing the request from ever reaching the controller.
8
Golden rules

When deciding where a check belongs, ask yourself: does this rule concern the whole system, or only certain actions?

If it concerns the whole system, think middleware. If it is closer to specific controllers or actions, think filter.

💡
Middleware is the outer gate, while a filter stands closer to the inner gate.
💡
Concerns like bans, authentication, and logging are a natural fit for middleware.
💡
Rules like plans, stock checks, and feature access fit filters better.
💡
Turning everything into middleware or everything into filters is not the right approach.
MINI QUIZ
Which of the following is the more correct pairing?
Ban check → Filter
System-wide forced sign-out → Middleware
Action-specific business rule → Middleware
Product stock check → Middleware on every request