c#中的AOP面向切面编程

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))]//他和TypeFilter一样都是做了特性实例的一个ioc容器的封装,只是ServiceFilter需要注册,上面的不需要注册。
        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 });
        }
    }
}

基于TypeFilter和ServiceFilter自定义封装的特性,[CustomFilterFactory(typeof(AopActionFilterAttribute))]来调用,不过AopActionFilterAttribute也需要注册

public class CustomFilterFactoryAttribute : Attribute, IFilterFactory
{
    private readonly Type type;
    public CustomFilterFactoryAttribute(Type type)
    {
        this.type = type;
    }
    public bool IsReusable => true;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return (IFilterMetadata)serviceProvider.GetService(type);
    }
}

演示效果

扩展: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);
        }
    }
}

 五、权限验证:AuthorizeAttribute

http协议是无状态请求,每个人都可以访问网页,但有点页面或方法不想别人访问就需要通过cookie或session来记录一种状态,这种状态叫访问权限
访问权限有两个步骤:1,鉴权-----》2,授权;需要在【Program.cs】启用

app.UseAuthentication();//鉴权:在请求中获取用户信息
app.UseAuthorization();//授权:拿用户信息去判断是否有权访问

配置鉴权

builder.Services.AddAuthentication(option => { //配置鉴权,选择用哪种方式来鉴权
    option.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;//默认方式:cookie
    option.DefaultChallengeScheme= CookieAuthenticationDefaults.AuthenticationScheme;
    option.DefaultSignInScheme= CookieAuthenticationDefaults.AuthenticationScheme;
    option.DefaultSignOutScheme= CookieAuthenticationDefaults.AuthenticationScheme;
    option.DefaultForbidScheme= CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, option => {
    option.LoginPath = "/Home/Login";//如果没有找到用户---表示鉴权失败,跳转到登录页面,先登录角色
    option.AccessDeniedPath = "/Home/Privacy";//第二步有用户信息,角色授权,如果无权限访问跳转到没有权限提示页面。
});

给方法设置权限验证

//[Authorize]//默认授权方式(参数1,:授权方式;参数2:角色授权)
//[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]//指定授权方式,所有角色可访问
//[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme, Roles = "admin")] //必须是管理员admin,
//[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme, Roles = "admin1")] //一个角色表示并且关系,表示必须包含admin和admin1都存在才能访问。
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme, Roles = "Admin,Teache")]  //多个角色表示或的关系,只需要存在一个都可以访问
public IActionResult Index()
{
    var user = HttpContext.User;//获取页面信息,用户信息,测试
    return View();
}

创建一个实体类,用于登录测试

public class CurrentUser
{
    public int Id { get; set; }
    [Display(Name = "用户名")]
    public string? Name { get; set; }
    public string? Account { get; set; }
    [Display(Name = "密码")]
    public string? Password { get; set; }

    [DataType(DataType.EmailAddress)]
    [Display(Name = "E-mail")]
    public string? Email { get; set; }
    public DateTime LoginTime { get; set; }
}

创建用于登录页后台

[HttpGet]
public IActionResult Login()
{
    return View();
}
[HttpPost]//提交登录信息
public async Task<IActionResult> Login(string name, string password)
{
    if ("admin".Equals(name) && "123456".Equals(password))
    {
        var claims = new List<Claim>()//用户信息集合
                    {
                        new Claim("Userid","1"),
                        new Claim(ClaimTypes.Role,"Admin"),
                        new Claim(ClaimTypes.Role,"User"),
                        new Claim(ClaimTypes.Name,$"{name}--来自于Cookies"),
                        new Claim(ClaimTypes.Email,$"net@163.com"),
                        new Claim("password",password),//可以写入任意数据
                        new Claim("Account","Administrator"),
                        new Claim("role","admin"),
                         new Claim("QQ","110120119")
                    };
        ClaimsPrincipal userPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, "Customer"));//记录用户信息
        HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, userPrincipal, new AuthenticationProperties
        {//包用户信息写入cookie,参数1是鉴权方式
            ExpiresUtc = DateTime.UtcNow.AddMinutes(30),//cookie过期时间:30分钟

        }).Wait();
        var user = HttpContext.User;
        return base.Redirect("/Home/Index");
    }
    else
    {
        base.ViewBag.Msg = "用户或密码错误";
    }
    return await Task.FromResult<IActionResult>(View());
}

登录页前台

@using Advanced.NET6.Project.Models;

@model CurrentUser
@{
    ViewBag.Title = "登录";
}

<h2>登录</h2>
<div class="row">
    <div class="col-md-8">
        <section id="loginForm">
            @using (Html.BeginForm("Login", "Home", new { sid = "123456", Account = "admin" },
            FormMethod.Post, true, new { @class = "form-horizontal", role = "form" }))
            {
                @Html.AntiForgeryToken()
                <hr />
                @Html.ValidationSummary(true) 
                <div class="mb-3 row"> 
                    @Html.LabelFor(m => m.Name, new { @class = "col-sm-1 col-form-label" }) 
                    <div class="col-md-6">
                        @Html.TextBoxFor(m => m.Name, new { @class = "form-control", @placeholder="请输入您的用户名" })
                    </div>
                </div>
                <div class="mb-3 row">
                    @Html.LabelFor(m => m.Password, new { @class = "col-md-1 control-label" })
                    <div class="col-md-6">
                        @Html.PasswordFor(m => m.Password, new { @class = "form-control",@placeholder="请输入密码" })
                    </div>
                </div>
                <div class="mb-3 row">
                    <div class="col-md-offset-2 col-md-6"> 
                        <button type="submit" class="btn btn-primary mb-3">登录</button>
                        @base.ViewBag.Msg
                    </div>
                </div>
            }
        </section>
    </div>
</div> 

策略授权:如果不配置,系统默认可以角色授权Roles = "角色名"

builder.Services.AddAuthorization(option => { //配置授权
    option.AddPolicy("rolePolicy", policyBuilder => { //添加授权策略(“取策略名”,委托授权方式)
        policyBuilder.RequireRole("Admin"); //授权角色
        policyBuilder.RequireClaim("Account");//必须包含某一个Claim键值对,键和值都可以做授权判断

        policyBuilder.RequireAssertion(context =>//业务逻辑授权
        {
            bool bResult = context.User.HasClaim(c => c.Type == ClaimTypes.Role)//用户信息,键必须包含这个Role
               && context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Role)).Value == "Admin"//值必须有的用户
               && context.User.Claims.Any(c => c.Type == ClaimTypes.Name);//Claim的键必须包含Name

            return bResult;
        });

        policyBuilder.AddRequirements(new QQEmailRequirement());//指定对象做验证返回布尔值
    });
});

 调用时需要改为字段策略

[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme, Policy = "rolePolicy")]//指定策略授权

AddRequirements是指定对象做验证,他还可以传IAuthorizationRequirement接口的一个数组,做第三方数据验证会用到。

public class QQEmailRequirement: IAuthorizationRequirement
{
    //我们可以创建一个帮助类实现这个接口,他其实就是个空接口,里面什么都不写,通过类或者接口直接映射到帮助类,创建QQHandler
}

创建一个第三方数据的模拟类库

namespace UserServiceClass
{
    public interface IUserService
    {
        public bool Validata(string userId, string qq);
    }
    public class UserService : IUserService
    {
        public bool Validata(string userId, string qq)
        {
            //在这里去链接数据库去校验这个QQ是否正确
            return true;
        }
    }
}

最后创建一个帮助类做验证的业务逻辑

using Microsoft.AspNetCore.Authorization;
using UserServiceClass;

namespace Advanced.NET6.Project.Utility
{
    public class QQHandler : AuthorizationHandler<QQEmailRequirement>//QQEmailRequirement这个对象会找到泛型的一个实现类QQHandler
    {//帮助类,继承抽象的泛型父类,必须传递一个能接收IAuthorizationRequirement接口的类型
        private IUserService _UserService;
        public QQHandler(IUserService userService)//IOC容器依赖注入把IUserService注入进来
        {
            this._UserService = userService;
        }
        //实现抽象类,这里可以做验证
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, QQEmailRequirement requirement)
        {
            if (context.User.Claims.Count() == 0)//判断是否有数据
            {
                return Task.CompletedTask;//异步任务处理的,返回已经完成的对象
            }
            //查询
            string userId = context.User.Claims.First(c => c.Type == "Userid").Value;
            string qq = context.User.Claims.First(c => c.Type == "QQ").Value;
            //验证
            if (_UserService.Validata(userId, qq))
            {
                context.Succeed(requirement); //验证通过了
            }
            return Task.CompletedTask;//返回已经完成的对象
        }
    }
}

另外需要将以上用到的类和接口依赖注入到IOC容器才能使用【Program.cs】

builder.Services.AddTransient<IUserService, UserService>();//模拟的第三方数据
builder.Services.AddTransient<IAuthorizationHandler, QQHandler>();//依赖注入到IOC容器来,构造方法才能找到这些类型

 Filter生命周期

a.权限验证:进入到Authorization
b.开始缓存:ResourceFilter 中的 -OnResourceExecuting
c.开始创建控制器实例
d.日志记录方法前:ActionFilter 中的 - OnActionExecuting
e.执行控制器Action方法
f.日志记录方法后:ActionFilter 中的 - OnActionExecuted
g.结果生成前的扩展:ResultFilter 中的 - OnResultExecuting
h.响应结果前的补充:AlwaysRunResultFilter 中的 - OnResultExecuting
i.渲染视图
j.响应结果后补充:AlwaysRunResultFilter 中的 - OnResultExecuted
k.结果生成后的扩展:ResultFilter 中的 - OnResultExecuted
l.结束缓存:ResourceFilter中的 -OnResourceExecuted

默认执行顺序:全局-----》控制器------》方法
可以通过Order属性来控制某个特性的执行顺序,如Order=10,值为整数,值越小就优先执行,值越大就后执行

[CustomActionActionFilterAttribute(Order =-99)]//使用Order属性改变顺序,需要把特性定义为继承父类的关系

 

posted @ 2022-12-16 07:23  Akai_啊凯  阅读(682)  评论(0编辑  收藏  举报