ActionFilterAttribute
是ASP.NET MVC 框架引入的特性,随着ASP.NET MVC 的发展不断完善。ASP.NET MVC 框架于 2009 年发布,为开发者提供了基于 MVC 模式构建 Web 应用程序的方式,ActionFilterAttribute
作为其中重要的一部分,用于在控制器动作执行前后插入自定义逻辑。之后,随着ASP.NET Core 的推出,ActionFilterAttribute
也得到了延续和改进,更好地适应了现代 Web 开发的需求。
ActionFilterAttribute
是一种过滤器,它基于 AOP(面向切面编程)的思想。在ASP.NET MVC 和ASP.NET Core 中,过滤器是一种特殊的类,用于在请求处理管道的特定阶段执行代码。ActionFilterAttribute
主要有两个关键的方法:
OnActionExecuting
:在控制器动作执行之前调用。可以在此方法中对请求进行预处理,例如验证请求参数、记录日志等。如果在这个方法中设置了 filterContext.Result
属性,那么动作方法将不会被执行,直接返回该结果。OnActionExecuted
:在控制器动作执行之后调用。可以在此方法中对响应进行后处理,例如修改响应内容、记录响应时间等。以下是一个使用 ActionFilterAttribute
对接口入参进行优先判断的示例。假设我们有一个简单的 Web API,要求传入的参数 id
必须大于 0。
using System;
using System.Web.Http.Filters;
using System.Net;
using System.Net.Http;
// 自定义ActionFilterAttribute
public class ParameterValidationFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
// 获取请求参数
var id = actionContext.ActionArguments.ContainsKey("id") ? (int?)actionContext.ActionArguments["id"] : null;
// 验证参数
if (id == null || id <= 0)
{
// 参数验证失败,返回错误信息
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest,
"参数 id 必须大于 0。");
}
}
}
// 控制器
public class MyController : System.Web.Http.ApiController
{
[ParameterValidationFilter]
public IHttpActionResult Get(int id)
{
// 只有参数验证通过才会执行到这里
return Ok($"你传入的 id 是: {id}");
}
}
ParameterValidationFilterAttribute
:继承自 ActionFilterAttribute
,重写 OnActionExecuting
方法。在该方法中,获取请求参数 id
,并验证其是否大于 0。如果验证失败,设置 actionContext.Response
属性,返回一个包含错误信息的 HTTP 响应。MyController
:在 Get
方法上应用 [ParameterValidationFilter]
特性,这样在执行 Get
方法之前,会先执行 ParameterValidationFilterAttribute
的 OnActionExecuting
方法进行参数验证。只有参数验证通过,才会继续执行 Get
方法的内容。这个示例展示了如何使用 ActionFilterAttribute
对接口的入参进行优先判断,确保只有符合条件的请求才能执行接口内的内容。
在 .NET 里,除了 ActionFilterAttribute
所在的过滤器体系,有一些具备相似功能或者可用于实现相似逻辑的库与机制,下面为你详细介绍:
Microsoft.AspNetCore.Authorization
(ASP.NET Core);System.Web.Mvc
(ASP.NET MVC)。using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[Authorize(Roles = "Admin")]
public class AdminController : Controller
{
public IActionResult Index()
{
return View();
}
}
在这个示例中,AuthorizeAttribute
充当授权过滤器,它保证只有具备 “Admin” 角色的用户才能访问 AdminController
的 Index
方法。
Microsoft.AspNetCore.Mvc.Core
(ASP.NET Core);System.Web.Mvc
(ASP.NET MVC)。csharp
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
// 记录日志
Console.WriteLine($"An exception occurred: {context.Exception.Message}");
// 返回统一的错误响应
context.Result = new ObjectResult("An error occurred. Please try again later.")
{
StatusCode = 500
};
}
}
[CustomExceptionFilter]
public class MyController : Controller
{
public IActionResult Index()
{
throw new Exception("Something went wrong!");
}
}
在这个例子中,CustomExceptionFilterAttribute
是自定义的异常过滤器,它会捕获 Index
方法抛出的异常,记录日志并返回统一的错误响应。
Microsoft.AspNetCore.Mvc.Core
(ASP.NET Core);System.Web.Mvc
(ASP.NET MVC)。csharp
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
public class CustomResultFilterAttribute : ResultFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
// 在结果执行前添加响应头
context.HttpContext.Response.Headers.Add("X-Custom-Header", "Custom Value");
base.OnResultExecuting(context);
}
}
[CustomResultFilter]
public class MyController : Controller
{
public IActionResult Index()
{
return Ok("Hello, World!");
}
}
在这个示例中,CustomResultFilterAttribute
是自定义的结果过滤器,它会在 Index
方法返回结果之前添加一个自定义的响应头。
Microsoft.AspNetCore.Mvc.Core
(ASP.NET Core)。using Microsoft.AspNetCore.Mvc.Filters;
using System.Threading.Tasks;
public class CustomPipelineFilter : IAsyncResourceFilter
{
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{
// 在资源执行前处理
// ...
var resultContext = await next();
// 在资源执行后处理
// ...
}
}
在这个例子中,CustomPipelineFilter
实现了 IAsyncResourceFilter
接口,可在资源执行前后进行处理。
Castle.Core.dll
Castle.Core.dll
算是具备与 ActionFilterAttribute
类似功能的库,下面从几个方面为你分析:
Castle.Core.dll
是 Castle 项目中的核心库,该项目提供了一系列的工具和框架,其中的动态代理(Dynamic Proxy)组件和 ActionFilterAttribute
一样,都能实现面向切面编程(AOP)的功能。而 ActionFilterAttribute
是 ASP.NET 中用于在控制器动作执行前后插入自定义逻辑的特性,也是 AOP 思想的一种体现。
ActionFilterAttribute
:在 ASP.NET 框架里,它是基于过滤器管道来工作的。当请求进入 MVC 或 Web API 应用时,框架会按特定顺序执行不同类型的过滤器,ActionFilterAttribute
可在动作方法执行前后执行代码。Castle.Core
的动态代理:借助运行时生成代理类,将横切关注点(如日志记录、事务管理等)织入到目标对象的方法调用中。它在不修改目标对象代码的情况下,为其添加额外功能。ActionFilterAttribute
:主要用于 ASP.NET 应用中控制器动作的预处理和后处理,像参数验证、日志记录、性能监控等,一般在 Web 开发场景下使用。Castle.Core
:可用于各类 .NET 应用程序,不仅能在 Web 应用中使用,还能在桌面应用、服务应用等场景使用。它能实现日志记录、事务管理、缓存、安全检查等横切关注点,具有更广泛的适用性。下面是使用 Castle.Core
实现简单 AOP 功能的示例:
using Castle.DynamicProxy;
using System;
// 定义一个拦截器类
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine($"Before method {invocation.Method.Name}");
invocation.Proceed();
Console.WriteLine($"After method {invocation.Method.Name}");
}
}
// 定义一个接口
public interface IService
{
void DoSomething();
}
// 实现接口
public class MyService : IService
{
public void DoSomething()
{
Console.WriteLine("Doing something...");
}
}
class Program
{
static void Main()
{
// 创建代理生成器
var proxyGenerator = new ProxyGenerator();
// 创建拦截器实例
var interceptor = new LoggingInterceptor();
// 创建目标对象
var service = new MyService();
// 生成代理对象
var proxy = proxyGenerator.CreateInterfaceProxyWithTarget(service, interceptor);
// 调用代理对象的方法
proxy.DoSomething();
}
}
代码解释
LoggingInterceptor
类实现了 IInterceptor
接口,在 Intercept
方法中定义了在目标方法执行前后要执行的逻辑。MyService
类实现了 IService
接口,代表目标对象。Main
方法中,使用 ProxyGenerator
生成代理对象,并将拦截器和目标对象关联起来。调用代理对象的方法时,拦截器的逻辑就会被执行。从上述分析可以看出,Castle.Core.dll
能实现和 ActionFilterAttribute
类似的 AOP 功能,不过它的应用场景更为广泛。
Autofac.Extras.DynamicProxy
是它的一个扩展,结合了 Autofac 的依赖注入能力与 Castle DynamicProxy 的 AOP 功能。它允许你在应用程序中使用 AOP 模式,将横切关注点(如日志记录、事务管理等)应用到服务的方法调用上。using Autofac;
using Autofac.Extras.DynamicProxy;
using Castle.DynamicProxy;
using System;
// 定义拦截器
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine($"Before method: {invocation.Method.Name}");
invocation.Proceed();
Console.WriteLine($"After method: {invocation.Method.Name}");
}
}
// 定义服务接口
public interface IMyService
{
void DoWork();
}
// 实现服务
public class MyService : IMyService
{
public void DoWork()
{
Console.WriteLine("Doing work...");
}
}
class Program
{
static void Main()
{
var builder = new ContainerBuilder();
// 注册拦截器
builder.RegisterType();
// 注册服务并应用拦截器
builder.RegisterType()
.As()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(LoggingInterceptor));
var container = builder.Build();
var service = container.Resolve();
service.DoWork();
}
}
using PostSharp.Aspects;
using System;
// 定义切面
[Serializable]
public class LoggingAspect : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
Console.WriteLine($"Before method: {args.Method.Name}");
}
public override void OnExit(MethodExecutionArgs args)
{
Console.WriteLine($"After method: {args.Method.Name}");
}
}
// 应用切面
[LoggingAspect]
public class MyClass
{
public void MyMethod()
{
Console.WriteLine("Method is executing...");
}
}
class Program
{
static void Main()
{
var obj = new MyClass();
obj.MyMethod();
}
}
NLog.Extensions.MicrosoftLogging
是它与 Microsoft 日志框架集成的扩展。虽然它主要用于日志记录,但可以通过配置和自定义实现类似 ActionFilterAttribute
的功能,例如在方法执行前后记录日志。using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;
using System;
public class MyService
{
private readonly ILogger _logger;
public MyService(ILogger logger)
{
_logger = logger;
}
public void DoSomething()
{
_logger.LogInformation("Before doing something");
Console.WriteLine("Doing something...");
_logger.LogInformation("After doing something");
}
}
class Program
{
static void Main()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging(loggingBuilder =>
{
loggingBuilder.ClearProviders();
loggingBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
loggingBuilder.AddNLog();
});
serviceCollection.AddTransient();
var serviceProvider = serviceCollection.BuildServiceProvider();
var myService = serviceProvider.GetRequiredService();
myService.DoSomething();
}
}
这些库或机制都以不同的方式实现了面向切面编程的思想,能够在方法执行前后插入自定义逻辑,与 ActionFilterAttribute
的功能有相似之处。