AOP,即面向切面编程,可以在不修改源代码的情况下,为程序动态地添加功能。通过横切技术,将影响多个类的公共行为(如日志记录、性能统计、安全控制、异常处理、事务处理等)封装到一个可重用的模块中。以此来减少系统的重复代码,降低模块的耦合度,提高程序的可重用性、可操作性和可维护性。
AOP在.NET Core中的应用场景非常广泛,包括但不限于:
- 日志记录:记录系统运行的详细信息,便于问题追踪和性能分析。
- 性能统计:监控和统计方法的执行时间,帮助开发者优化系统性能。
- 安全控制:进行身份验证和授权,确保系统安全。
- 事务处理:管理数据库事务,确保数据的一致性和完整性。
- 异常处理:全局捕获和处理异常,避免程序崩溃,并记录异常信息以便后续分析。
一、过滤器 Filter
过滤器(Filter)
- Authorization Filter:用于身份验证和授权,是五种Filter中优先级最高的。
- Resource Filter:在Action执行前后进行资源处理,如资源缓存、防盗链等。
- Exception Filter:用于全局的异常处理,捕获并处理未捕获的异常。
- Action Filter:在Action执行前后进行拦截,常用于执行操作日志、参数验证等。
- Result Filter:在Action结果返回前后进行拦截,常用于对结果进行格式化、大小写转换等操作。
1、五大常用Filter
1.1、Authorization Filter
权限控制过滤器 通过 Authonization Filter 可以实现复杂的权限角色认证
、登陆授权
等操作
public class AuthonizationFilter :Attribute,IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
//这里可以做复杂的权限控制操作
if (context.HttpContext.User.Identity.Name != "1") //简单的做一个示范
{
//未通过验证则跳转到无权限提示页
RedirectToActionResult content = new RedirectToActionResult("NoAuth", "Exception", null);
context.Result = content;
}
}
}
1.2、Resource Filter
资源过滤器 可以通过Resource Filter 进行资源缓存
、防盗链
等操作
public class ResourceFilter : Attribute,IResourceFilter
{
public void OnResourceExecuted(ResourceExecutedContext context)
{
// 执行完后的操作
}
public void OnResourceExecuting(ResourceExecutingContext context)
{
// 执行中的过滤器管道
}
}
1.3、Exception Filter
通过Execption Filter 过滤器可以进行全局的异常日志收集
等操作。
public class ExecptionFilter : Attribute, IExceptionFilter
{
private ILogger<ExecptionFilter> _logger;
//构造注入日志组件
public ExecptionFilter(ILogger<ExecptionFilter> logger)
{
_logger = logger;
}
public void OnException(ExceptionContext context)
{
//日志收集
_logger.LogError(context.Exception, context?.Exception?.Message??"异常");
}
}
1.4、Action Filter
可以通过ActionFilter 拦截 每个执行的方法进行一系列的操作,比如:执行操作日志
、参数验证
,权限控制
等一系列操作
public class ActionFilter : Attribute, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
//执行完成....
}
public void OnActionExecuting(ActionExecutingContext context)
{
//执行中...
}
}
1.5、Result Filter
结果过滤器,可以对结果进行格式化、大小写转换等一系列操作。
public class ResultFilter : Attribute, IResultFilter
{
public void OnResultExecuted(ResultExecutedContext context)
{
// 在结果执行之后调用的操作...
}
public void OnResultExecuting(ResultExecutingContext context)
{
// 在结果执行之前调用的一系列操作
}
}
2、过滤器的注册方式
全局:将作用于所有请求的action
controller:将作用于这个controller下的所有action
action:作用于单个action
2.1、全局注册
builder.Services.AddMvc(options =>
{
options.Filters.Add<FilterAttribute>();
});
2.2、Controller 注册方式
[Route("api/[controller]")]
[ApiController]
[Filter]//添加过滤器特性,该控制器的所有方法自动走该过滤器
public class FilterAttributeController : ControllerBase
{
[HttpGet]
[Filter]//添加过滤器特性,该方法自动走该过滤器
public string G_Method1()
{
return "1";
}
}
2.3、Action 注册方式
如2.2
3、TypeFilter 和 ServiceFilter
3.1、ServiceFilter
通过ServiceFilter从容器中检索你的ActionFilter,并且注入到需要的地方。
//在Program.cs中必须先注册MyFilter这个过滤器到服务容器中
builder.Services.AddSigleton<MyFilter>();
//控制器方法上用ServiceFilter注入自定义过滤器
[ServiceFilter(typeof(MyFilter))]
public IActionResult Index()
{
//一些处理
...
return Ok();
}
3.2、TypeFilter
使用Microsoft.Extensions.DependencyInjection.ObjectFactory 对类型进行实例化。
//控制器方法上用TypeFilter注入自定义过滤器
[TypeFilter(typeof(MyFilter),IsReusable = true)]
public IActionResult Index()
{
//一些处理
...
return Ok();
}
它和ServiceFilter的区别是它不从DI容器中创建实例,所以不需要在服务容器中进行注册就能使用。TypeFilter是通过ObjectFactory来创建实例的,ObjectFactory是ActivatorUtilities.CreateFactory创建出来的委托。
ServiceFilter与TypeFilter总结
这两个特性都可以实现自定义过滤器的局部注入,但有不同:
ServiceFilter和TypeFilter都实现了IFilterFactory
ServiceFilter需要对自定义的Filter进行注册,TypeFilter不需要
ServiceFilter的Filter生命周期源自于您如何注册,而TypeFilter每次都会创建一个新的实例
4、Filter特性
4.1、内置的过滤器特性
以下列举部分常见的内置过滤器特性,我们可以通过继承这些类,快速实现自己的特性类。
ActionFilterAttribute :行为过滤器特性
ExceptionFilterAttribute:异常过滤器特性
ResultFilterAttribute:结果过滤器特性
FormatFilterAttribute:响应格式化过滤器特性
ServiceFilterAttribute:用来局部使用过滤器的特性
TypeFilterAttribute:也是用来局部注入过滤器的特性
4.2、 编写一个过滤器特性
使用ActionFilterAttribute这个内置过滤器特性,自定义一个过滤器特性,来实现添加任意响应头部信息
/// <summary>
/// 继承了一个 ActionFilterAttribute,这个抽象类继承了什么接口呢?
/// </summary>
//ResponseHeaderAttribute是一个自定义特性,[ResponseHeader]
public class ResponseHeaderAttribute : ActionFilterAttribute
{
private readonly string _name;
private readonly string _value;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
public ResponseHeaderAttribute(string name, string value)
{
(_name, _value) = (name, value); //元组赋值法
}
/// <summary>
/// 结果过滤器里边的 结果被执行前
/// </summary>
/// <param name="context"></param>
public override void OnResultExecuting(ResultExecutingContext context)
{
//添加响应头,键值对信息 (你对HTTP协议了解多少??)
context.HttpContext.Response.Headers.Add(_name, _value);
base.OnResultExecuting(context);
}
}
测试以上定义的自定义过滤器
//在控制器上应用
[ResponseHeader("Filter-Header", "Filter Value")]
public class UserController : ControllerBase
{
public IActionResult Index() =>
Content("请通过浏览器F12调试工具查看输出的响应头信息");
// ...
//也可以在方法上面应用
[ResponseHeader("Another-Filter-Header", "Another Filter Value")]
public IActionResult Multiple() =>
Content("请通过浏览器F12调试工具查看输出的响应头信息");
}
二、中间件 Middleware
1、概念
中间件是一种处理HTTP请求和响应的独立组件,它位于客户端请求与服务器端处理程序之间。每个中间件组件负责执行特定任务,并可以选择将请求传递给管道中的下一个中间件,直至请求被最终处理或响应被返回给客户端。
执行流程
2、Map、Use 和 Run
Map 用来定义一个管道可以处理哪些请求,Use 和 Run 用来定义管道,一个管道由若干个 Use 和一个 Run 组成,每个 Use 引入一个中间件,而 Run 用来执行最终的核心应用逻辑。
2.1、使用use添加中间件
//使用use添加单个中间件
app.Use(async (context, next) =>
{
context.Request.Headers.Add("head1", "11");
await next.Invoke();
Console.WriteLine(context.Request.Headers["head1"]);
Console.WriteLine("22");
});
2.2、使用map匹配对应路由的中间件
//使用map为指定路由的action添加中间件,以下就是一个管道
app.Map("/api/FilterAttribute/test1", appbuilders =>
{
//第一个中间件
appbuilders.Use(async (context, next) =>
{
context.Request.Headers.Add("head3", "33");
await next.Invoke();
Console.WriteLine(context.Request.Headers["head3"]);
Console.WriteLine("44");
});
//第二个中间件
appbuilders.Use(async (context, next) =>
{
context.Request.Headers.Add("head5", "55");
await next.Invoke();
Console.WriteLine(context.Request.Headers["head5"]);
Console.WriteLine("66");
});
//第三个中间件,也是终止
appbuilders.Run(async context =>
{
Console.WriteLine("/FilterAttribute这个路径下的中间件结束了");
});
});
当执行时/api/FilterAttribute下的test1方法时,打印如下
/FilterAttribute这个路径下的中间件结束了
55
66
33
44
注://如果在一个中间件中使用 ctx.Response.WriteAsync 向客户端发送响应,就不能再执行 next.Invoke 把请求转到其他中间件了,因为其他中间件中有可能对 Response 进行了更改,比如修改响应状态码、修改报文头或者向响应报文中写入其他数据, 这样就会造成响应报文体被损坏的问题,如果在向报文体中写入内容后,又执行 next.Invoke 是不推荐的行为
2.3、使用MapWhen判断是否执行中间件
app.MapWhen(context =>
{
return 1 == 1;//如果返回true,则执行下面的中间件
}, appbuilder =>
{
appbuilder.Use(async (context, next) =>
{
Console.WriteLine("77");
await next.Invoke();
Console.WriteLine("88");
});
appbuilder.Run(async context =>
{
Console.WriteLine("77");
});
});
3、自定义中间件
public class MyMiddleware1
{
//需要有RequestDelegate、InvokeAsync方法参数是HttpContext类型
private readonly RequestDelegate _requestDelegate;
public MyMiddleware1(RequestDelegate requestDelegate)
{
_requestDelegate = requestDelegate;
}
public async Task InvokeAsync(HttpContext context)
{
Console.WriteLine("MyMiddleware1执行前");
await _requestDelegate.Invoke(context);
Console.WriteLine("MyMiddleware1执行后");
}
}
//在program.cs中
app.UseMiddleware<MyMiddleware1>();
4、常用内置中间件
中间件名称 | 英文名称 | 作用 |
---|---|---|
身份验证中间件 | UseAuthentication | 提供身份验证支持 |
异常处理中间件 | UseExceptionHandler | 异常/错误处理 |
跨域中间件 | UseCors | 配置跨域资源共享 |
路由中间件 | UseRouting | 定义和约束请求路由 |
静态文件中间件 | UseStaticFiles | 为静态文件和目录浏览提供服务提供支持 |
授权中间件 | UseAuthorization | 用于授权用户访问安全资源 |
终结点路由中间件 | UseEndpoints | 定义和约束请求路由,用于匹配路由的终端 |
此外,还有Session会话,Cookie,响应缓存等中间件。
中间件顺序