过滤器及拦截器

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}");
    }
}

代码解释

  1. 自定义过滤器 ParameterValidationFilterAttribute:继承自 ActionFilterAttribute,重写 OnActionExecuting 方法。在该方法中,获取请求参数 id,并验证其是否大于 0。如果验证失败,设置 actionContext.Response 属性,返回一个包含错误信息的 HTTP 响应。
  2. 控制器 MyController:在 Get 方法上应用 [ParameterValidationFilter] 特性,这样在执行 Get 方法之前,会先执行 ParameterValidationFilterAttribute 的 OnActionExecuting 方法进行参数验证。只有参数验证通过,才会继续执行 Get 方法的内容。

这个示例展示了如何使用 ActionFilterAttribute 对接口的入参进行优先判断,确保只有符合条件的请求才能执行接口内的内容。

其他

在 .NET 里,除了 ActionFilterAttribute 所在的过滤器体系,有一些具备相似功能或者可用于实现相似逻辑的库与机制,下面为你详细介绍:

1. 授权过滤器(Authorization Filters)

  • 相关 DLLMicrosoft.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 方法。

2. 异常过滤器(Exception Filters)

  • 相关 DLLMicrosoft.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 方法抛出的异常,记录日志并返回统一的错误响应。

3. 结果过滤器(Result Filters)

  • 相关 DLLMicrosoft.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 方法返回结果之前添加一个自定义的响应头。

4. 管道过滤器(Pipeline Filters)

  • 相关 DLLMicrosoft.AspNetCore.Mvc.Core(ASP.NET 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 功能,不过它的应用场景更为广泛。

1. Autofac.Extras.DynamicProxy

  • 概述:Autofac 是一个流行的依赖注入容器,而 Autofac.Extras.DynamicProxy 是它的一个扩展,结合了 Autofac 的依赖注入能力与 Castle DynamicProxy 的 AOP 功能。它允许你在应用程序中使用 AOP 模式,将横切关注点(如日志记录、事务管理等)应用到服务的方法调用上。
  • 原理:借助 Autofac 的容器来管理对象的生命周期,同时利用 Castle DynamicProxy 在运行时生成代理类,把拦截器逻辑织入到目标对象的方法调用中。
  • 使用案例
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();
    }
}

2. PostSharp

  • 概述:PostSharp 是一个强大的 AOP 框架,它通过编译时织入的方式将横切关注点集成到代码中。与运行时生成代理不同,PostSharp 在编译时修改 IL 代码,把额外的逻辑插入到目标方法中。
  • 原理:在代码编译期间,PostSharp 分析代码中的特性(Attributes),并根据这些特性对 IL 代码进行修改,从而实现 AOP 功能。
  • 使用案例
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();
    }
}

3.NLog.Extensions.MicrosoftLogging

  • 概述:NLog 是一个用于 .NET 平台的日志记录库,NLog.Extensions.MicrosoftLogging 是它与 Microsoft 日志框架集成的扩展。虽然它主要用于日志记录,但可以通过配置和自定义实现类似 ActionFilterAttribute 的功能,例如在方法执行前后记录日志。
  • 原理:借助 Microsoft 的日志抽象层,将日志记录逻辑集成到应用程序中。可以在不同的代码位置插入日志记录语句,实现对方法执行过程的监控。
  • 使用案例
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 的功能有相似之处。

你可能感兴趣的:(c#)