首页 > 编程语言 >c#中的AOP面向切面编程

c#中的AOP面向切面编程

时间:2022-12-16 07:33:05浏览次数:42  
标签:控制器 Console WriteLine c# 切面 context AOP using public

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

相关文章

  • Chrome
    ChromeUsagecat>docker-compose.yml<<-'EOF'#https://hub.docker.com/r/browserless/chrome#Port:3000version:"3"services:chrome:image:browserl......
  • 01-html&CSS
    常用标签介绍  font<body><!--字体标签需求1:在网页上显示我是字体标签,并修改字体为宋体,颜色为红色。font标签是字体标签,它可以用来修改文本的字体,颜......
  • pychartlt图表处理
    1 安装模块pipinstall-ihttps://pypi.mirrors.ustc.edu.cn/simple/pyechartspipinstall-ihttps://pypi.mirrors.ustc.edu.cn/simple/echarts-countries-pypk......
  • docker学习
    一、介绍docker容器是一种轻量级、可移植、自包含的软件打包技术,是一种应用程序,可以在几乎任何地方以相同的方式运行。下载安装地址:https://hub.docker.com/windows......
  • 「REMAKE C++」Day 2
    Day2今天的学习主要是阅读C++Primer第2章结尾以及第3章。decltype类型指示符decltype(f())sum=x;sum类型与f返回类型相同。decltype((variable)),......
  • ssm报错Could not open JDBC Connection for transaction; nested exception is com.m
    HTTPStatus500-Requestprocessingfailed;nestedexceptionisorg.springframework.transaction.CannotCreateTransactionException:CouldnotopenJDBCConnecti......
  • 使用EFcore连接Oracle数据时出现的错误及解决方案(Oracle版本指定错误)
    使用EFcore连接Oracle数据时出现的错误及解决方案(Oracle版本指定错误)程序环境1,DotNet版本.net6.02,EFcore版本:Microsoft.EntityFrameWorkCore6.0.11Microsof......
  • C# 命名空间和程序集 小记
    前言本文只是为了复习,巩固,和方便查阅,一些知识点的详细知识会通过相关链接和文献标记出来。命名空间1.1概念可以把命名空间看做字符串,他加在类名或类型名前面并且通过......
  • Python OpenCV
    OpenCV准备工作Python版本3.6OpenCV版本3.4.1.15condacreate-nOpenCV3.6python=3.6pipinstallopencv-python==3.4.1.15pipinstalldlib==19.6.1pytho......
  • Docker 创建安装运行.NetCore/Consul/minio脚本命令相关
    ==dockernetworkcreate-dbridgejasonnet1:.NetCore相关项目//===========01:登录认证授权sudodockerrm容器id-fsudodockerrmiv_authserve......