AOP(Aspct Oriented Programming)在不修改源代码的基础上,通过特性的方式添加一些业务逻辑。就是一些特性类
在asp.net core中通过Filter库来支持AOP的,(六种)支持
一、资源缓存:IResourceFilter和IAsyncResourceFilter(异步)
天生为缓存而生,应用场景就是用于缓存。(需要实现两个方法)以特性的方式调用在控制器方法上
代码编译顺序:OnResourceExecuting------》控制器构造方法-----》控制器普通方法-----OnResourceExecuted
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; namespace Advanced.NET6.Project.Utility.Filters { public class AopResourceFiltersAttribute : Attribute, IResourceFilter //1.特性,2.缓存接口(需要实现两个方法) { private static Dictionary<string, object> dt = new Dictionary<string, object>();//定义字典来记录缓存 public void OnResourceExecuting(ResourceExecutingContext context)//参数控制器是的实例 { Console.WriteLine("在控制器的构造方法和普通方法,前执行"); string key = context.HttpContext.Request.Path;//获取请求路径 if (dt.ContainsKey(key)) //如果存在个路径,表示有记录缓存 { context.Result = (IActionResult)dt[key];//保存不变,返回路径,IActionResult表示一个控制器的试图类型 } } public void OnResourceExecuted(ResourceExecutedContext context) { Console.WriteLine("在控制器的构造方法和普通方法执行完成,后执行"); string key = context.HttpContext.Request.Path;//获取请求路径 dt[key] = context.Result;//记录缓存,把控制器方法保存起来 } } }
控制器调用,以特性的方式定义在方法上
using Advanced.NET6.Project.Models; using Advanced.NET6.Project.Utility.Filters; using Microsoft.AspNetCore.Mvc; using System.Diagnostics; namespace Advanced.NET6.Project.Controllers { public class HomeController : Controller { public HomeController() { Console.WriteLine("控制器构造方法,已执行。。。"); } [AopResourceFilters]//调用缓存特性 public IActionResult Index() { Console.WriteLine("控制器index方法,已执行。。。"); ViewBag.datetime = DateTime.Now.ToString("yyyy-MM-dd ss");//测试数据,关注秒的变化 return View(); } public IActionResult Privacy() { return View(); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } } }
测试数据
<h2>后台index方法输出的时间:@ViewBag.datetime</h2> <h2>前台定义的时间无缓存变化:@DateTime.Now.ToString("yyyy-MM-dd ss");</h2>
扩展:IAsyncResourceFilter(异步版本)两个方法变一个方法,还是多线程处理的
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; namespace Advanced.NET6.Project.Utility.Filters { public class AopResourceFiltersAttribute : Attribute, IAsyncResourceFilter //1.特性,2.异步缓存(只有一个方法) { private static Dictionary<string, object> dt = new Dictionary<string, object>();//定义字典来记录缓存 public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) { Console.WriteLine("在控制器的构造方法和普通方法,前执行"); string key = context.HttpContext.Request.Path;//获取请求路径 if (dt.ContainsKey(key)) //如果存在个路径,表示有记录缓存 { context.Result = (IActionResult)dt[key];//保存不变,返回路径,IActionResult表示一个控制器的试图类型 } else { Console.WriteLine("在控制器的构造方法和普通方法执行完成,后执行"); var resource = await next.Invoke();//这句话就是去执行控制器构造方法和普通方法 dt[key] = resource.Result;//记录缓存,把控制器方法保存起来 } } } }
二、方法的前后记录:IActionFilter和IAsyncActionFilter(异步)
就是方法前后的记录,就是执行在一个普通方法之前之后的两个方法,适用于记录日志
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; namespace Advanced.NET6.Project.Utility.Filters { public class AopActionFilterAttribute : Attribute, IActionFilter //1.特性,2.日志记录log { private readonly ILogger<AopActionFilterAttribute> logger; public AopActionFilterAttribute(ILogger<AopActionFilterAttribute> logger) { this.logger = logger; } public void OnActionExecuting(ActionExecutingContext context)//参数是控制器的实例 { Console.WriteLine("执行于方法之前"); var controllerName =context.HttpContext.GetRouteValue("Controller");//获取控制器名 var actionName = context.HttpContext.GetRouteValue("action");//获取方法名 var para = context.HttpContext.Request.QueryString.Value;//获取参数 logger.LogInformation($"马上执行{controllerName}控制器下的{actionName}方法,参数为:{para}"); } public void OnActionExecuted(ActionExecutedContext context) { Console.WriteLine($"执行于方法之后"); var result = Newtonsoft.Json.JsonConvert.SerializeObject(context.Result); logger.LogInformation($"方法的返回结果为:{result}"); } } }
调用
using Advanced.NET6.Project.Models; using Advanced.NET6.Project.Utility.Filters; using Microsoft.AspNetCore.Mvc; using System.Diagnostics; namespace Advanced.NET6.Project.Controllers { public class HomeController : Controller { public HomeController() { Console.WriteLine("控制器构造方法,已执行。。。"); } //[AopActionFilter]//不能这样调用ioc容器的问题后面在讲 [TypeFilter(typeof(AopActionFilterAttribute))]//需要把类名写全,因为特性的构造方法有参数,所以需要这样获取类型 //[ServiceFilter(typeof(AopActionFilterAttribute))] public IActionResult Index(int id) { Console.WriteLine("控制器index方法,已执行。。。"); ViewBag.datetime = DateTime.Now.ToString("yyyy-MM-dd ss");//测试数据,关注秒的变化 ViewData["user"] = Newtonsoft.Json.JsonConvert.SerializeObject(new { id = id, name = "张三", age = 18 }); object str = "测试返回结果的日志信息"; return View(str); } public IActionResult Privacy() { return View(); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } } }
演示效果
扩展:IAsyncActionFilter 两个方法变一个方法,异步版本,调用时改为 [TypeFilter(typeof(AopAsyncActionFilterAttribute))]
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; namespace Advanced.NET6.Project.Utility.Filters { public class AopAsyncActionFilterAttribute : Attribute, IAsyncActionFilter //异步编程日志记录log { private readonly ILogger<AopAsyncActionFilterAttribute> logger; public AopAsyncActionFilterAttribute(ILogger<AopAsyncActionFilterAttribute> logger) { this.logger = logger; } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { Console.WriteLine("执行于方法之前"); var controllerName = context.HttpContext.GetRouteValue("Controller");//获取控制器名 var actionName = context.HttpContext.GetRouteValue("action");//获取方法名 var para = context.HttpContext.Request.QueryString.Value;//获取参数 logger.LogInformation($"马上执行{controllerName}控制器下的{actionName}方法,参数为:{para}"); var Controller = await next.Invoke();//表示正在执行控制器里的方法 Console.WriteLine($"执行于方法之后"); var result = Newtonsoft.Json.JsonConvert.SerializeObject(Controller.Result); logger.LogInformation($"方法的返回结果为:{result}"); } } }
三、结果生成前后扩展:IResultFilter和IAsyncResultFilter(异步)
就是在方法返回结果之前和之后的两个扩展方法,就是方法渲染试图前后插入的两个方法,用于渲染结果的统一处理,或者是json格式的统一处理
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; namespace Advanced.NET6.Project.Utility.Filters { public class AopResultFilterAttribute : Attribute, IResultFilter { public void OnResultExecuting(ResultExecutingContext context) { Console.WriteLine("执行于返回结果之前"); if (context.Result is JsonResult) { JsonResult jr = (JsonResult)context.Result; context.Result = new JsonResult(new AjaxResult() { Success = true, Message = "OK", Data = jr.Value }); } } public void OnResultExecuted(ResultExecutedContext context) { Console.WriteLine($"执行于返回结果之后"); } } public class AjaxResult//实体类 { public bool Success { get; set; } public string? Message { get; set; } public object? Data { get; set; } } }
调用
using Advanced.NET6.Project.Models; using Advanced.NET6.Project.Utility.Filters; using Microsoft.AspNetCore.Mvc; using System.Diagnostics; namespace Advanced.NET6.Project.Controllers { public class HomeController : Controller { public HomeController() { Console.WriteLine("控制器构造方法,已执行。。。"); } [AopResultFilter] public IActionResult Index() { Console.WriteLine("控制器index方法,已执行。。。"); return Json(new { id=1,name="张三",age=18 }); } public IActionResult Privacy() { return View(); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } } }
扩展:异步版本 IAsyncResultFilter
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using System; namespace Advanced.NET6.Project.Utility.Filters { public class AopResultFilterAttribute : Attribute, IAsyncResultFilter { public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { Console.WriteLine("执行于返回结果之前"); if (context.Result is JsonResult) { JsonResult jr = (JsonResult)context.Result; context.Result = new JsonResult(new AjaxResult() { Success = true, Message = "OK", Data = jr.Value }); } var Controller = await next.Invoke();//表示正在执行控制器里的方法 Console.WriteLine($"执行于返回结果之后"); } } public class AjaxResult//实体类 { public bool Success { get; set; } public string? Message { get; set; } public object? Data { get; set; } } }
IAlwaysRun是响应结果的补充:IAlwaysRunResultFilter和IAsyncAlwaysRunResultFilter
using Microsoft.AspNetCore.Mvc.Filters; namespace Advanced.NET6.Project.Utility.Filters { public class IAsyncAlwaysRunAttribute: Attribute, IAsyncAlwaysRunResultFilter { public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { Console.WriteLine("响应结果前的代码补充"); var Controller = await next.Invoke();//表示正在执行控制器里的方法 Console.WriteLine("响应结果后的代码补充"); } } }
ActionFilterAttribute是上面3个Filter的结合体
public class AopAllActionFilterAttribute : ActionFilterAttribute //抽象类:都是控制器前后执行的方法 { //实现需要的抽象类即可,注意:当我们执行异步版本时,同步版本filter失效。C#推荐我们所以异步版本。 }
特性注册在方法上,一个方法生效;注册在控制器上,控制器所有方法生效;全局注册所有控制器方法都生效【Program.cs】不想生效的控制器或方法标记特性:[AllowAnonymous]
builder.Services.AddControllersWithViews(options => { options.Filters.Add<AopAllActionFilterAttribute>();//通过委托来全局注册 });
匿名支持:[AllowAnonymous]是系统给我们的取消注册,但有的不能生效(IResourceFilter和IActionFilter,IResultFilter),只能自己扩展
public class AopAllowAnonymousAttribute : Attribute { //使用定义一个匿名特性【AopAllowAnonymous】,就是自定义特性,在方法执行前用if判断拦截。 }
在每个Filter的方法中添加判断,取消注册就用[AopAllowAnonymous]来代替[AllowAnonymous]
if (context.ActionDescriptor.EndpointMetadata.Any(a => a.GetType().Equals(typeof(AopAllowAnonymousAttribute)))) { return;//查询当前控制器或方法是否存在特性[AopAllowAnonymous],存在就退出。 }
四、异常处理:IExceptionFilter和IAsyncExceptionFilter
异常处理的特性(ExceptionFilter + 中间件注册 = 所有异常处理),避免我们写大量的try 和 catch
public class AopExceptionFilterAttribute : Attribute, IExceptionFilter, IAsyncExceptionFilter//异常处理(同步和异步) { public void OnException(ExceptionContext context)//同步:有异常就会执行 { throw new NotImplementedException(); } public Task OnExceptionAsync(ExceptionContext context)//异步,如果有异步方法,不会执行同步 { throw new NotImplementedException(); } }
中间件【Program.cs】
///如果Http请求中的Response中的状态不是200,就会进入Home/Error中; app.UseStatusCodePagesWithReExecute("/Home/Error/{0}");//只要不是200 都能进来 //下面这个是自己拼装一个Reponse 输出 app.UseExceptionHandler(errorApp => { errorApp.Run(async context => { context.Response.StatusCode = 200; context.Response.ContentType = "text/html"; await context.Response.WriteAsync("<html lang=\"en\"><body>\r\n"); await context.Response.WriteAsync("ERROR!<br><br>\r\n"); var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>(); Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"); Console.WriteLine($"{exceptionHandlerPathFeature?.Error.Message}"); Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"); if (exceptionHandlerPathFeature?.Error is FileNotFoundException) { await context.Response.WriteAsync("File error thrown!<br><br>\r\n"); } await context.Response.WriteAsync("<a href=\"/\">Home</a><br>\r\n"); await context.Response.WriteAsync("</body></html>\r\n"); await context.Response.WriteAsync(new string(' ', 512)); // IE padding }); });
异常特性封装
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ViewFeatures; namespace Advanced.NET6.Project.Utility.Filters { public class AopExceptionFilterAttribute : Attribute, IExceptionFilter//异常处理 { private readonly IModelMetadataProvider _metadataProvider; public AopExceptionFilterAttribute(IModelMetadataProvider metadataProvider) { this._metadataProvider = metadataProvider;//将驱动程序注入进来,错误信息带人错误页需要 } public void OnException(ExceptionContext context)//同步:有异常就会执行 { if (context.ExceptionHandled == false) { if (IsAjaxRequest(context.HttpContext.Request))//判断是否ajax请求json { context.Result = new JsonResult(new { succeess = false, message = context.Exception.Message }); } else { ViewResult result= new ViewResult { ViewName="~/views/shared/error.cshtml" };//错误页面 result.ViewData = new ViewDataDictionary(_metadataProvider, context.ModelState);//错误信息带人错误页,需要驱动程序 result.ViewData.Add("Exception", context.Exception); context.Result = result;//断路器--主要对Result赋值就不继续往后执行 } context.ExceptionHandled = true;//表示当前异常处理过 } } private bool IsAjaxRequest(HttpRequest request) { string header = request.Headers["X-Requested-With"]; return "XMLHttpRequest".Equals(header); } } }
标签:控制器,Console,WriteLine,c#,切面,context,AOP,using,public From: https://www.cnblogs.com/longxinyv/p/16984739.html