Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Filters

Các loại Filter

Filters cho phép chạy code tại các điểm cụ thể trong pipeline execution của MVC.

┌─────────────────────────────────────────────────────────────────────┐
│                    MVC REQUEST PIPELINE                             │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  1. Authorization Filter                                             │
│     ↓                                                               │
│  2. Resource Filter (OnExecuting)                                   │
│     ↓                                                               │
│  3. Model Binding                                                   │
│     ↓                                                               │
│  4. Action Filter (OnExecuting)                                     │
│     ↓                                                               │
│  5. Action executes                                                 │
│     ↓                                                               │
│  6. Action Filter (OnExecuted)                                      │
│     ↓                                                               │
│  7. Result Filter (OnExecuting)                                     │
│     ↓                                                               │
│  8. Result executes                                                 │
│     ↓                                                               │
│  9. Result Filter (OnExecuted)                                      │
│     ↓                                                               │
│ 10. Resource Filter (OnExecuted)                                   │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

1. Authorization Filter

public class CustomAuthorizationFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var user = context.HttpContext.User;
        
        if (!user.Identity.IsAuthenticated)
        {
            context.Result = new UnauthorizedResult();
        }
    }
}

// Sử dụng
[ServiceFilter(typeof(CustomAuthorizationFilter))]
public class ProductsController : ControllerBase { }

2. Resource Filter

public class TrackRequestFilter : IAsyncResourceFilter
{
    private readonly ILogger<TrackRequestFilter> _logger;

    public TrackRequestFilter(ILogger<TrackRequestFilter> logger)
    {
        _logger = logger;
    }

    public async Task OnResourceExecutingAsync(ResourceExecutingContext context)
    {
        _logger.LogInformation("Resource executing");
        // Thực thi trước khi resource (controller) được gọi
        context.HttpContext.Items["StartTime"] = DateTime.UtcNow;
    }

    public async Task OnResourceExecutedAsync(ResourceExecutedContext context)
    {
        var startTime = (DateTime)context.HttpContext.Items["StartTime"];
        _logger.LogInformation($"Resource executed in {(DateTime.UtcNow - startTime).TotalMilliseconds}ms");
    }
}

3. Action Filter

public class LogActionFilter : IActionFilter
{
    private readonly ILogger<LogActionFilter> _logger;

    public LogActionFilter(ILogger<LogActionFilter> logger)
    {
        _logger = logger;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        _logger.LogInformation("Action executing: {Controller}.{Action}", 
            context.Controller.GetType().Name,
            context.ActionDescriptor.Name);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        _logger.LogInformation("Action executed");
    }
}

4. Exception Filter

public class GlobalExceptionFilter : IExceptionFilter
{
    private readonly ILogger<GlobalExceptionFilter> _logger;

    public GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger)
    {
        _logger = logger;
    }

    public void OnException(ExceptionContext context)
    {
        _logger.LogError(context.Exception, "Unhandled exception");

        var result = new
        {
            error = "An error occurred",
            message = context.Exception.Message,
            traceId = context.HttpContext.TraceIdentifier
        };

        context.Result = new JsonResult(result)
        {
            StatusCode = 500
        };
        
        context.ExceptionHandled = true;
    }
}

5. Result Filter

public class CacheResultFilter : IResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (!context.HttpContext.Response.HasStarted)
        {
            context.HttpContext.Response.Headers["Cache-Control"] = "no-cache";
        }
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Post-processing
    }
}

Filter Types Comparison

Filter TypeImplementsRunsUse Case
AuthorizationIAuthorizationFilterĐầu tiênCheck authentication/authorization
ResourceIResourceFilterTrước & sau model bindingCaching, performance tracking
ActionIActionFilterTrước & sau actionLogging, validation
ExceptionIExceptionFilterKhi exception xảy raError handling
ResultIResultFilterTrước & sau result executionOutput formatting, caching

Filters vs Middleware

Filters

  • ✅ Chỉ áp dụng cho MVC/Razor Pages
  • ✅ Có access đến ActionContext (model binding results, etc.)
  • ✅ Có thể bind services từ DI
  • ✅ Thực thi sau khi route đã được xác định

Middleware

  • ✅ Áp dụng cho toàn bộ pipeline (bao gồm static files)
  • ✅ Thực thi trước khi routing xác định endpoint
  • ✅ Phù hợp cho cross-cutting concerns không liên quan đến MVC

Khi nào dùng?

// Middleware - Cho toàn bộ app
app.Use(async (context, next) =>
{
    // Log mọi request
    await next();
});

// Filter - Cho MVC actions cụ thể
[ServiceFilter(typeof(MyActionFilter))]
public class ProductsController : ControllerBase { }

Đăng ký Filters

1. Global

builder.Services.AddControllersWithViews()
    .AddMvcOptions(options =>
    {
        options.Filters.Add(new GlobalExceptionFilter());
    });

2. Controller/Action Level

[ControllerLevelFilter]
public class ProductsController : ControllerBase
{
    [ActionLevelFilter]
    public IActionResult Get() { }
}

3. Service Filter

// Đăng ký trong DI
builder.Services.AddScoped<MyFilter>();

// Sử dụng
[ServiceFilter(typeof(MyFilter))]
public IActionResult Get() { }

4. Type Filter

// Không cần đăng ký trong DI
[TypeFilter(typeof(MyFilter))]
public IActionResult Get() { }