Sự giống nhau nhưng lại khác nhau
Nếu nhìn qua, Middleware và Filter rất giống nhau.
Ví dụ:
Middleware:
public class LoggingMiddleware
{
private readonly RequestDelegate _next;
public LoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
Console.WriteLine("Before");
await _next(context);
Console.WriteLine("After");
}
}
Filter:
public class LoggingFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine("Before");
}
public override void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine("After");
}
}
Nó giống nhau:
- Đều xử lý before/after
- Đều log được request
- Đều có thể chặn request
- Đều có thể xử lý exception
Chính vì quá giống nhau, nên nhiều người bắt đầu nghĩ:
Filter chính là Middleware dành cho Controller.
Không hẳn.
Thực tế Middleware và Filter là hai pipeline hoàn toàn khác nhau.
Middleware - tầng bảo vệ toàn cục
Middleware hoạt động ở tầng HTTP Pipeline.
Nói đơn giản:
Mọi HTTP Request đi vào server, đều đi qua Middleware trước.
Kể cả request đó:
- API
- Swagger
- Static File
- Image
- CSS
- Javascript
Flow sẽ như thế này:
HTTP Request
↓
Middleware
↓
Middleware
↓
Middleware
↓
Endpoint
Đây là lý do Middleware thường được dùng cho:
- Authentication
- Logging
- Exception handling
- CORS
- Rate limiting
Vì nó hoạt động ở tầng toàn cục.
await _next(contex) thật ra đang làm gì?
Ví dụ:
await _next(context);
Nghĩa là:
Cho request đi tiếp xuống Middleware tiếp theo
Nếu không gọi _next(context):
public async Task InvokeAsync(HttpContext context)
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Unauthorized");
}
Pipeline sẽ dừng ngay tại middleware đó. Request không bao giờ tới Controller
Đây gọi là Short-circuit pipeline. Rất nhiều middleware hoạt động theo kiểu:
- Authentication fail
- Rate limit vượt quá giới hạn
- Request invalid
- Cache hit
Lúc này sẽ response luôn, không đi tiếp nữa.
Controller chưa phải nơi request đi vào đầu tiên
Đây là hiểu lầm rất phổ biến. Nhiều người nghĩ request đi thẳng vào Controller.
Không.
Trước khi chạm vào Controller, request đã và phải đi qua rất nhiều tầng bên dưới.
Flow thật sự đã được nói ở bài viết "Controller hoạt động thế nào?"
HTTP Request
↓
Middleware Pipeline
↓
Routing
↓
Authentication
↓
Authorization
↓
MVC Pipeline
↓
Filters
↓
Controller Action
Filter chỉ xuất hiện khi và chỉ khi request đã:
- Match route thành công
- Xác định được Controller/Action cần chạy
Nếu request không match route:
404 Not Found
Lúc này, Filter thậm chí còn chưa tồn tại. Trong khi Middleware vẫn chạy bình thường.
Middleware và Filter khác nhau ở đâu?
Nói đơn giản:
Middleware xử lý ở tầng HTTP toàn cục. Filter xử lý bên trong MVC Pipeline.
Ví dụ:
Middleware giống như bảo vệ ngoài cổng của toà nhà. Filter sẽ giống bảo vệ riêng cho từng phòng trong toà nhà đó.
Middleware sẽ không biết:
- Action nào sắp chạy
- Model gì được bind
- Controller nào được gọi
Nó chỉ biết:
Có một HTTP Request vừa đi vào server
Ngược lại, Filter lúc này đã biết rất nhiều thứ:
- Controller nào đang chạy
- Action nào được gọi
- Parameter truyền vào là gì
- Authorization theo Action
- Before/After Action
Vì sao Middleware chạy nhưng Filter không chạy?
Đây là bug kinh điển chắc nhiều người đã từng gặp.
Ví dụ:
Request
↓
Middleware chạy
↓
Routing fail
↓
404 Not Found
Lúc này:
- Middleware vẫn execute bình thường
- Nhưng Filter không hề chạy
Lý do: vì request chưa bao giờ đi được và vào tới MVC Pipeline.
Đây cũng là lý do đôi khi debug mới thấy:
Ủa sao middleware log được mà filter không chạy?
Exception Middleware và Exception Filter không giống nhau
Exception Middleware:
Bắt exception ở tầng HTTP Pipeline
Exception Filter:
Bắt exception bên trong MVC Pipeline
Ví dụ exception xảy ra trước khi vào MVC, lúc này Authentication Middleware nổ exception thì Exception Filter hoàn toàn không bắt được.
Ngược lại, nếu exception xảy ra bên trong Controller Action:
Controller
↓
throw Exception
Lúc này, Exception Filter mới có cơ hội xử lý.
Bẫy khi đặt logic sai tầng
Ví dụ:
- Validate JWT trong Filter
- Log request body trong Controller
- Exception handling bằng ActionFilter
- Cache logic nằm trong Action
Mọi thứ vẫn chạy được. Nhưng architecture sẽ bắt đầu rối.
Và một quy tắc ra đời:
- Logic toàn cục -> Middleware
- Logic liên quan MVC/Action -> Filter
Kết luận
Middleware và Filter nhìn khá giống nhau.
Cả hai đều:
- Log
- Validate
- Bắt exception
- Xử lý before/after
Nhưng thực tế, nó nằm ở hai pipeline hoàn toàn khác nhau bên trong ASP.NET Core
- Middleware hoạt động ở tầng HTTP toàn cục
- Filter chỉ tồn tại sau khi request đi vào MVC Pipeline
Hiểu được flow này sẽ giúp xác định chính xác:
- Request đang kẹt ở đâu
- Logic nên đặt tầng nào
- Vì sao Filter không chạy
- Vì sao Middleware không bắt được exception
- Vì sao request chưa chạm được Controller
Middleware và Filter không chỉ giúp nhau, tương tác với nhau và đang phối hợp cùng nhau phía dưới một HTTP Request.
Anonymous
Jun 03, 2026Bài viết rất hay, cám ơn bạn