1. 过滤器在C#中的核心价值与应用场景
在C#开发中,过滤器(Filter)是一种强大的横切关注点处理机制。它允许我们在不修改核心业务逻辑的情况下,对方法调用或请求处理流程进行拦截和增强。想象一下你正在开发一个电商系统——每个订单处理都需要验证用户权限、记录操作日志、检查输入参数合法性。如果把这些代码直接写在业务方法里,很快就会变成一团乱麻。过滤器正是解决这类问题的银弹。
过滤器模式本质上属于AOP(面向切面编程)的实践,它通过将通用功能从业务代码中剥离出来,实现了关注点分离。在.NET生态中,过滤器主要应用于以下几个典型场景:
- Web请求处理:ASP.NET Core中的Action Filter可以处理请求前后的逻辑,比如权限验证(AuthorizationFilter)、模型验证(ActionFilter)、结果格式化(ResultFilter)等
- 异常处理:通过ExceptionFilter统一捕获和处理异常,避免try-catch块污染业务代码
- 日志记录:用过滤器自动记录方法入参、返回值、执行时间等诊断信息
- 缓存管理:通过过滤器实现缓存读取和更新,比如对GET请求结果进行缓存
- 性能监控:在方法执行前后插入计时逻辑,统计执行耗时
提示:过滤器与中间件(Middleware)的区别在于粒度不同——中间件处理的是HTTP管道级别的逻辑,而过滤器处理的是控制器和Action级别的逻辑。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. ASP.NET Core中的过滤器体系解析
2.1 过滤器管道的工作原理
ASP.NET Core的过滤器管道是一个精密的拦截机制,当请求到达Controller的Action时,会依次经过以下过滤器类型(按执行顺序排列):
- Authorization Filters:最先执行,处理认证和授权(如
[Authorize]) - Resource Filters:在授权之后、模型绑定之前执行,可短路请求(如
IActionFilter) - Action Filters:在Action方法执行前后触发(如
OnActionExecuting和OnActionExecuted) - Exception Filters:捕获Action或Result中抛出的异常
- Result Filters:在Action结果执行前后触发(如
OnResultExecuting和OnResultExecuted)
这个管道机制可以通过一个简单的例子来理解:假设有个获取用户信息的API,请求处理流程就像穿过一系列安检门——先检查证件(Authorization),然后检查行李(Resource),进入操作区(Action),最后打包结果(Result),任何环节发现问题都可以直接返回。
2.2 内置过滤器类型详解
AuthorizationFilter实战
csharp复制public class CustomAuthFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
// 检查请求头中的API密钥
if (!context.HttpContext.Request.Headers.TryGetValue("X-API-KEY", out var apiKey))
{
context.Result = new UnauthorizedResult();
return;
}
// 验证密钥有效性
if (!IsValidApiKey(apiKey))
{
context.Result = new JsonResult(new { error = "Invalid API Key" })
{
StatusCode = StatusCodes.Status403Forbidden
};
}
}
private bool IsValidApiKey(string apiKey) { /* 验证逻辑 */ }
}
ActionFilter的典型应用
csharp复制public class LogActionFilter : IActionFilter
{
private readonly ILogger _logger;
public LogActionFilter(ILogger<LogActionFilter> logger)
{
_logger = logger;
}
public void OnActionExecuting(ActionExecutingContext context)
{
// Action执行前记录入参
_logger.LogInformation($"Executing {context.ActionDescriptor.DisplayName}");
_logger.LogInformation($"Arguments: {JsonSerializer.Serialize(context.ActionArguments)}");
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Action执行后记录结果和耗时
_logger.LogInformation($"Executed {context.ActionDescriptor.DisplayName}");
}
}
3. 自定义过滤器的开发实践
3.1 创建异常处理过滤器
异常处理是Web开发中的常见需求,下面是一个增强版的异常过滤器实现:
csharp复制public class ApiExceptionFilter : IExceptionFilter
{
private readonly IDictionary<Type, Action<ExceptionContext>> _exceptionHandlers;
private readonly IWebHostEnvironment _env;
public ApiExceptionFilter(IWebHostEnvironment env)
{
_env = env;
// 配置特定异常的处理方式
_exceptionHandlers = new Dictionary<Type, Action<ExceptionContext>>
{
{ typeof(ValidationException), HandleValidationException },
{ typeof(NotFoundException), HandleNotFoundException },
{ typeof(UnauthorizedAccessException), HandleUnauthorizedAccessException }
};
}
public void OnException(ExceptionContext context)
{
HandleException(context);
}
private void HandleException(ExceptionContext context)
{
var exceptionType = context.Exception.GetType();
if (_exceptionHandlers.ContainsKey(exceptionType))
{
_exceptionHandlers[exceptionType].Invoke(context);
return;
}
// 默认异常处理
if (!_env.IsDevelopment())
{
context.Result = new JsonResult(new
{
error = "An error occurred while processing your request"
})
{
StatusCode = StatusCodes.Status500InternalServerError
};
context.ExceptionHandled = true;
}
}
private void HandleValidationException(ExceptionContext context)
{
var exception = context.Exception as ValidationException;
context.Result = new BadRequestObjectResult(new
{
error = "Validation failed",
details = exception.Errors
});
context.ExceptionHandled = true;
}
// 其他异常处理方法...
}
3.2 实现性能监控过滤器
性能监控是另一个常见场景,下面这个过滤器可以记录方法执行时间:
csharp复制public class BenchmarkFilter : IActionFilter
{
private Stopwatch _stopwatch;
public void OnActionExecuting(ActionExecutingContext context)
{
_stopwatch = Stopwatch.StartNew();
}
public void OnActionExecuted(ActionExecutedContext context)
{
_stopwatch.Stop();
var actionName = context.ActionDescriptor.DisplayName;
var elapsedMs = _stopwatch.ElapsedMilliseconds;
// 记录到诊断系统
