概念
AOP全称Aspect Oriented Programming意为面向切面编程,也叫做面向方法编程,是通过预编译方式和运行期动态代理的方式实现不修改源代码的情况下给程序动态统一添加功能的技术。
AOP技术利用一种称为“横切”的技术,剖解开封装对象的内部,将影响多个类的公共行为封装到一个可重用的模块中,并将其命名为Aspect切面。所谓的切面,简单来说就是与业务无关,却为业务模块所共同调用的逻辑,将其封装起来便于减少系统的重复代码,降低模块的耦合度,有利用未来的可操作性和可维护性。
利用AOP可以对业务逻辑各个部分进行隔离,从而使业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率。
AOP的使用场景主要包括日志记录、性能统计、安全控制、事务处理、异常处理等。
在.net core中的运用
- 中间件
- Filter
中间件(第三方容器组件)
- 基于名称的注入
- 属性注入
- 子容器
- 基于动态代理的AOP
核心扩展点
public interface IServiceProviderFactory<TContainerBuilder>
使用Autofac实现注入
NuGet
Autofac.Extensions.DependencyInjection
Autofac.Extras.DynamicProxy
namespace HelloApi.Service
{
public interface IMyService
{
void ShowCode();
}
public class MyService : IMyService
{
public void ShowCode()
{
Console.WriteLine($"MyService.ShowCode:{GetHashCode()}");
}
}
public class MyServiceV2 : IMyService
{
public MyNameService NameService { get; set; }
public void ShowCode()
{
Console.WriteLine($"MyServiceV2.ShowCode:{GetHashCode()}, NameService is null ? { NameService == null }");
}
}
public class MyNameService
{
}
}
using System;
using Castle.DynamicProxy;
namespace HelloApi.Service
{
public class MyInterceptor:IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine($"Intercept before,Method:{invocation.Method.Name}");
invocation.Proceed();//让具体的方法执行
Console.WriteLine($"Intercept after,Method:{invocation.Method.Name}");
}
}
}
namespace HelloApi
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory()) //追加这行代码 (注册第三方容器的入口)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Autofac.Extras.DynamicProxy;
using HelloApi.Service;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace HelloApi
{
public class Startup
{
public ILifetimeScope AutofacContainer { get; private set; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
public void ConfigureContainer(ContainerBuilder builder)
{
//builder.RegisterType<MyService>().As<IMyService>();
////命名注册
//builder.RegisterType<MyServiceV2>().Named<IMyService>("service2");
////属性注册
//builder.RegisterType<MyNameService>();
//builder.RegisterType<MyServiceV2>().As<IMyService>().PropertiesAutowired();
//AOP
builder.RegisterType<MyInterceptor>();
builder.RegisterType<MyServiceV2>().As<IMyService>().PropertiesAutowired().InterceptedBy(typeof(MyInterceptor)).EnableInterfaceInterceptors();
////子容器
//builder.RegisterType<MyNameService>().InstancePerMatchingLifetimeScope("myscope");
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//根容器
this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
//获取没有进行命名注册的服务
var service = this.AutofacContainer.Resolve<IMyService>();
service.ShowCode();
////获取命名注册之后的服务
//var serviceNamed = this.AutofacContainer.ResolveNamed<IMyService>("service2");
//serviceNamed.ShowCode();
////子容器
//using (var myscope = AutofacContainer.BeginLifetimeScope("myscope"))
//{
// var service0 = myscope.Resolve<MyNameService>();
// using(var scope = myscope.BeginLifetimeScope())
// {
// var service1 = scope.Resolve<MyNameService>();
// var service2 = scope.Resolve<MyNameService>();
// Console.WriteLine($"service1 == service0 ? {service1 == service0}");//service1 == service0 ? True
// Console.WriteLine($"service1 == service2 ? {service1 == service2}");//service1 == service2 ? True
// }
//}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Intercept before,Method:ShowCode
MyServiceV2.ShowCode:22799085, NameService is null ? True
Intercept after,Method:ShowCode
Filter-过滤器
ASP.NET Core 有以下五种Filter 可以使用:
- Authorization Filter:Authorization是五种Filter中优先级最高的,通常用于验证Request合不合法,不合法后面就直接跳过。
- Resource Filter:Resource是第二优先,会在Authorization之后,Model Binding之前执行。通常会是需要对Model加工处理才用。
- Exception Filter:异常处理的Filter。
- Action Filter:最常使用的Filter,封包进出都会经过它,使用上没什么需要特别注意的。跟Resource Filter很类似,但并不会经过Model Binding。
- Result Filter:当Action完成后,最终会经过的Filter。
Authonization 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;
}
}
}
Resource Filter
资源过滤器 可以通过Resource Filter 进行资源缓存、防盗链等操作。使用Resource Filter 要求实现IResourceFilter 抽象接口
public class ResourceFilter : Attribute,IResourceFilter
{
public void OnResourceExecuted(ResourceExecutedContext context)
{
// 执行完后的操作
}
public void OnResourceExecuting(ResourceExecutingContext context)
{
// 执行中的过滤器管道
}
}
Exception Filter
通过Execption Filter 过滤器可以进行全局的异常日志收集 等操作。
使用Execption Filter 要求实现IExceptionFilter 抽象接口 IExceptionFilter接口会要求实现OnException方法,当系统发生未捕获异常时就会触发这个方法。OnException方法有一个ExceptionContext异常上下文,其中包含了具体的异常信息,HttpContext及mvc路由信息。系统一旦出现未捕获异常后,比较常见的做法就是使用日志工具,将异常的详细信息记录下来,方便修正调试。
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??"异常");
}
}
Action Filter
作用:可以通过ActionFilter 拦截 每个执行的方法进行一系列的操作,比如:执行操作日志、参数验证,权限控制 等一系列操作。
使用Action Filter 需要实现IActionFilter 抽象接口,IActionFilter 接口要求实现OnActionExecuted 和OnActionExecuting 方法
public class ActionFilter : Attribute, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
//执行完成....
}
public void OnActionExecuting(ActionExecutingContext context)
{
//执行中...
}
}
Result Filter
结果过滤器,可以对结果进行格式化、大小写转换等一系列操作。
使用Result Filter 需要实现IResultFilter 抽象接口,接口要求实现 OnResultExecuting 方法 和OnResultExecuted 方法
- OnResultExecuting :Called before the action result executes. 在操作结果执行之前调用
- OnResultExecuted :Called after the action result executes. 在操作结果执行之后调用
public class ResultFilter : Attribute, IResultFilter
{
public void OnResultExecuted(ResultExecutedContext context)
{
// 在结果执行之后调用的操作...
}
public void OnResultExecuting(ResultExecutingContext context)
{
// 在结果执行之前调用的一系列操作
}
}
过滤器的注册方式
Action、Controller、全局
Action 注册方式
Action 注册方式是局部注册方式,针对控制器中的某个方法上标注特性的方式进行注册
[AuthonizationFilter()]
public IActionResult Index()
{
return View();
}
Controller 注册方式
[AuthonizationFilter()]
public class FirstController : Controller
{
private ILogger<FirstController> _logger;
public FirstController(ILogger<FirstController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return View();
}
}
全局注册方式
public void ConfigureServices(IServiceCollection services)
{
//全局注册异常过滤器
services.AddControllersWithViews(option=> {
option.Filters.Add<ExecptionFilter>();
});
services.AddSingleton<ISingletonService, SingletonService>();
}
TypeFilter 和 ServiceFilter 注册方式
TypeFilter 和ServiceFilter 的区别
- ServiceFilter和TypeFilter都实现了IFilterFactory
- ServiceFilter需要对自定义的Filter进行注册,TypeFilter不需要
- ServiceFilter的Filter生命周期源自于您如何注册,而TypeFilter每次都会创建一个新的实例
[TypeFilter(typeof(ExecptionFilter))]
public IActionFilter Index2()
{
return View();
}
[ServiceFilter(typeof(ExecptionFilter))]
public IActionFilter Index2()
{
return View();
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
Console.WriteLine("ConfigureServices");
services.AddControllersWithViews();
//services.AddControllersWithViews(option=> {
// option.Filters.Add<ExecptionFilter>();
//});
//注册过滤器服务,使用ServiceFilter 方式必须要注册 否则会报没有注册该服务的相关异常
services.AddSingleton<ExecptionFilter>();
}