首页 > 其他分享 >.net6 过滤器、管道模型

.net6 过滤器、管道模型

时间:2023-08-10 23:15:10浏览次数:42  
标签:string builder 管道 context new 过滤器 net6 public

管道处理模型

1、[中间件](https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-7.0)

  • 可以在典型应用中了解现有中间件的顺序,以及在哪里添加自定义中间件。 你可以完全控制如何重新排列现有中间件,或根据场景需要注入新的自定义中间件。

    • ExceptionHandler异常捕获中间件: 如果接下来的流程中出现了异常, 可以使用他捕获,

    • 不建议改变CORS之前的顺序, 容易出错

ASP.NET Core 中间件管道

  • program.cs中 app.Use的就是中间件

2、Filter过滤器

  • Filter过滤器就是AOP. 面向切面编程

  • AOP 面向切面编程, 就是把无关业务的逻辑, 作为业务前置、业务中、业务后置执行, 比如权限验证、记录日志等操作

  • 上述中间件执行完成之后进入执行Filter过滤器

  • .net core 6中有多重过滤器流程 称作管道模型, 使用中间件链接

    • AuthorizationFilters 权限验证过滤器

    • ResourceFilters 资源过滤器

      • 擅长缓存

    • Model Binding 模型绑定

      • 接口中传入的模型参数, 与模型进行绑定

    • ActionFilters 行为过滤器

      • 擅长模型验证

      • 日志记录

    • ExceptionFilters 异常过滤器

      • 线程捕获异常错误

    • ResultFilters结果过滤器

      • 擅长处理结果

(0)五大过滤器

过滤器 名称   功能
AuthorizationFilter 授权过滤器 1 权限验证
ResourceFilters 资源管理过滤 2 缓存
ActionFilters 行为过滤器 3 模型验证、日志记录
ExceptionFilters 异常过滤器 4 异常处理
ResultFilters 结果过滤器 5 结果处理

(1)自定义过滤器

  • context是当前应用程序上下文, 包括如下内容

namespace SecondDemo.Filters
{
    //自定义方法过滤器 -》 这样就获得了特性[CtmActionFilterAttribute]
    public class CtmActionFilterAttribute : Attribute, IActionFilter
    {
        //方法执行后
        public void OnActionExecuted(ActionExecutedContext context)
        {
            //throw new NotImplementedException();
            Console.WriteLine("方法执行后");
        }
        //方法执行中 、 前
        public void OnActionExecuting(ActionExecutingContext context)
        {
            //throw new NotImplementedException();
            Console.WriteLine("方法执行中");
        }
    }
}

(2)注册方式

  • 方法注册: 注册到方法上边

  • 类注册: 注册到控制器上边

  • 全局注册: 注册到Program.cs

1.方法注册
  • 直接把过滤器注册到方法上边

[HttpGet(Name ="测试自定义方法过滤器")]
[CtmActionFilter]
public void Get() 
{
    Console.WriteLine("大家好 执行了");
}
2.类注册
  • 直接把过滤器注册到类上边

  • 类下边所有方法, 全部共享

  • 相同类型的过滤器, 类注册的要先于方法注册的执行

  • 不同类型的过滤器, 只按照管道模型的顺序执行

    [CtmClassFilterClass]
    [ApiController]
    [Route("/api/[controller]/[action]")]
    public class TestFilterController:ControllerBase
    {
        [HttpGet(Name ="测试自定义方法过滤器")]
        public void Get() 
        {
            Console.WriteLine("大家好 执行了");
        }
    }
3.全局注册
  • 所有的控制器方法在执行之前都会进入该过滤器

  • 相同类型的过滤器, 全局注册的要先于其他两个注册的执行

//全局注册过滤器
builder.Services.AddControllers(opt => 
{
    opt.Filters.Add(typeof(CtmActionFilterEnvAttribute))
});
builder.Services.AddControllers(opt => 
{
    opt.Filters.Add<CtmActionFilterEnvAttribute>()
544454});
0.注册执行顺序
  • 全局注册, 数字越小越靠前执行

builder.Services.AddControllers(opt => 
                                { 
                                    opt.Filters.Add<CtmActionFilterEnv1Attribute>(1));
                                    opt.Filters.Add<CtmActionFilterEnv2Attribute>(2));
                                }

(4)filter的IOC

用过滤器, 建议使用过滤器构造函数注入, 作为方法特性, 当有属性需要作为构造器参数的时候, 如下例子

public class CtmAuthorizationFilterAttribute : Attribute, IAuthorizationFilter
    {
​
        private IActionsManager ActionsManager;
        public CtmAuthorizationFilterAttribute(IActionsManager actionsManager)
        {
            ActionsManager = actionsManager;
        }
}
  • 使用TypeFilter过滤器构造器注册, 先去把参数加入IOC容器, 再到相应的方法上注册为方法特性

    builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
    builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
    {
        builder.RegisterType<ActionsManager>().As<IActionsManager>();
    });
    [HttpGet]
    [TypeFilter(typeof(CtmAuthorizationFilterAttribute))]
    public string TestAuthorizationActionFilter(int userId, string userName)
    {
        //...
    }
  • 配合IOC容器注册使用ServiceFilter

    • 使用ServiceFilter, 无论是filter本身还是filter需要注入的参数, 都需要在构造IOC中注册

    builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
    builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
    {
        builder.RegisterType<ActionsManager>().As<IActionsManager>();
        builder.RegisterType<CtmActionFilterAttribute>();
    });
    [HttpGet]
    [ServiceFilter(typeof(CtmAuthorizationFilterAttribute))]
    public string TestAuthorizationActionFilter(int userId, string userName)
    {
        //...
    }

3、AuthorizationFilter授权过滤器

  • 是过滤器管道中第一个过滤器

    • 第一个过滤器, 还没有进行到类型绑定阶段Model Binding

    • 无法直接通过context上下文获取接口参数

  • 控制对方法的访问

  • 只能拦截执行前的, 不能拦截执行后的

namespace SecondDemo.Models
{
    //方法的信息
    public class Action
    {
        public int Id { get; set; }
        public string ControllerName { get; set; }
        public string ActionName { get; set; }
        /// <summary>
        /// 返回用户拥有的方法列表
        /// </summary>
        /// <param name="UserId"></param>
        /// <returns></returns>
        public static List<Models.Action> GetActionByUserId(int UserId)
        {
            if (UserId == 6)
            {
                return new List<Action> { new Action { ActionName = "Get", ControllerName = "TestAuthorizationController", Id = 1 } };
            }
            return default;
        }
    }
}
namespace SecondDemo.Models
{
    //用户的信息
    public class User
    {
        public int Id { get; set; }
        public string UserName { get; set; }
    }
}
​
namespace SecondDemo.Controllers
{
    [ApiController]
    [Route("/api/[controller]/[action]")]
    public class TestAuthorizationController : ControllerBase
    {
        [HttpGet]
        [CtmActionFilterClassAttribute]
        public string Get(int userId, string userName)
        {
            return userId.ToString();
        }
    }
}
namespace SecondDemo.Filters
{
    /// <summary>
    /// 鉴定用户权限
    /// </summary>
    public class CmtAuthorizationFilterAttribute : Attribute, IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            //获取请求参数列表
            string paramValue = context.HttpContext.Request.QueryString.Value;
            //获取字典<参数主键, 参数值>
            Dictionary<string, object> paramDic = paramValue.GetParam();
            //判断是否具有如下的ID
            if (paramDic.TryGetValue(AuthorizationConst.USER_ID, out object value))
            {
                //获取用户ID、用户的权限列表
                int userId = Convert.ToInt32(paramDic[AuthorizationConst.USER_ID]);
                List<Models.Action> actionsList = Models.Action.GetActionByUserId(userId);
                //获取当前请求的 接口/控制器名
                string actionName = context.RouteData.Values["action"].ToString();
                string controllerName = context.RouteData.Values["controller"].ToString();
                //对比当前接口/控制器名字是否相同
                if (!actionsList.Any(action => action.ActionName == actionName && action.ControllerName == controllerName))
                {
                    throw new Exception("用户无权限");
                }
                /*var sameAction = from action in actionsList
                            where action.ActionName == actionName
                            where action.ControllerName == controllerName
                            select action;
                if (sameAction == null)
                {
                    throw new Exception("用户无权限");
                }*/
            }
            throw new Exception("用户没有分配权限");
        }   
    }
}
namespace SecondDemo.Extensions
{
    public static class AuthorizationParameterExtensions
    {
        /// <summary>
        /// 增强string, 获取请求参数
        /// </summary>
        /// <param name="paramValue"></param>
        /// <returns></returns>
        public static Dictionary<string, object> GetParam(this string paramValue)
        {
            paramValue.Replace("?", "").Trim();
            //获取参数
            string[] paramValues = paramValue.Split("%");
            Dictionary<string, object> paraDic = new Dictionary<string, object>();
            //遍历切割方法请求路径, 获取到请求参数的key和值
            foreach (var param in paramValues)
            {
                paraDic.Add(param.Split("=")[0].Trim(), param.Split("=")[1].Trim());
            }
            return paraDic;
        }
​
    }

4、ResourceFilter资源过滤器

(1)短路器

  • 请求经过短路器直接返回给前端了

  • 不会继续在短路器后继续执行

  • ResouceExecutedcontext中, Result可以直接作为短路器使用

    • 只要给这个属性赋值, 直接短路

    • 类型是ActionResult, 常常给他一个JsonResult()的值

context.Result = new JsonResult();

(2)利用中间件缓存实现短路

  • IOC注入缓存中间件, 单例模式注入, 选用瞬态则导致缓存失效

//开启autofac
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
//使用autofac进行ioc注入
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{
    //注入缓存中间件, 以单例模式注入, 选用瞬态则导致缓存失效
    builder.RegisterType<MemoryCache>().As<IMemoryCache>().SingleInstance();
});
  • controller

[HttpGet]
[TypeFilter(typeof(CtmResourceFilterAttribute))]
public string TestResourceFilterAttribute()
{
    //...
}
  • Filter

namespace FilterDemo.Filters
{
    public class CtmResourceFilterAttribute : Attribute, IResourceFilter
    {
        //缓存
        private IMemoryCache memoryCache;
​
        public CtmResourceFilterAttribute(IMemoryCache memoryCache)
        {
            this.memoryCache = memoryCache;
        }
​
        public void OnResourceExecuted(ResourceExecutedContext context)
        {
            //拿到结束上次访问时的地址, 获取上一次访问的Api[Action]
            string path = context.HttpContext.Request.Path;
            //以上次访问的地址作为key, 将返回的数据存储到缓存中
            memoryCache.Set(CacheConst.REQUEST_PATH_KEY, path);            
        }
​
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            //获取当前的访问地址
            string path = context.HttpContext.Request.Path;
            //当前缓存中是否包含当前访问的地址
            if (memoryCache.TryGetValue(CacheConst.REQUEST_PATH_KEY, out object pathValue))
            {
                //如果包含, 则使用短路器, 接下来的步骤不执行了, 直接响应回去返回缓存的数据
                context.Result = pathValue as ObjectResult;
            }
        }
    }
}
  • 公众常量

public class CacheConst
{
    public const string REQUEST_PATH_KEY = "request:path";
}

5、ActionFilter行为过滤器

  • 适合搞模型验证, 日志记录

(1)操作日志例子

//开启autofac
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
//使用autofac进行ioc注入
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{
    builder.RegisterType<User>();
}
namespace FilterDemo.Filters
{
    public class CtmActionFilterAttribute : Attribute, IActionFilter
    {
        //日志组件
        private readonly ILogger<CtmActionFilterAttribute> _logger;
        //用户
        private readonly User user;
​
        //构造函数注入日志组件
        public CtmActionFilterAttribute(ILogger<CtmActionFilterAttribute> logger, User user)
        {
            _logger = logger;
            this.user = user;
        }
​
        public void OnActionExecuted(ActionExecutedContext context)
        {
            throw new NotImplementedException();
        }
​
        public void OnActionExecuting(ActionExecutingContext context)
        {
            //获取请求参数
            IDictionary<string, object?> arguments = context.ActionArguments;
            //获取请求路径
            string path = context.HttpContext.Request.Path;
            if (arguments.ContainsKey("user"))
            {
                User user = arguments["user"] as User;
                _logger.LogInformation($"{user.userName} is visting {path} at {DateTime.Now}");
            }
            else 
            {
                _logger.LogInformation($"{user.userName} is visting {path} at {DateTime.Now}");
            }
        }
    }
}
namespace FilterDemo.Controllers
{
    [ApiController]
    [Route("/api/[controller]/[action]")]
    public class TestController
    {
        [HttpGet]
        //使用过滤器构造器注入
        [TypeFilter(typeof(CtmActionFilterAttribute))]
        //配合IOC容器注册才能使用ServiceF
        [ServiceFilter(typeof(CtmActionFilterAttribute))]
        public string TestActionTypeFilter()
        {
            return "ok";
        }
    }
}

6、ExceptionFilter异常过滤器

  • 捕获错误信息

  • 作为接口方法特性, 可以在过滤器中捕获

  • 过滤器中记录错误日志, 并且使用短路器响应回去

例子

public class CtmExceptionFilterAttribute : Attribute, IExceptionFilter
{
    //日志
    private readonly ILogger<CtmExceptionFilterAttribute> logger;
    public CtmExceptionFilterAttribute(Logger<CtmExceptionFilterAttribute> logger)
    {
        this.logger = logger;
    }
​
    public void OnException(ExceptionContext context)
    {
        //获取异常信息
        string exceptionMessage = context.Exception.Message;
        //记录错误日志
        logger.LogError(exceptionMessage);
        //短路器返回, 错误信息
        context.Result = new ContentResult
        {
            Content = context.Exception.Message
        };
    }
}
[HttpGet]
[TypeFilter(typeof(CtmExceptionFilterAttribute))]
public string TestExceptionFiltterAttribute() 
{
    throw new Exception("test");
    return "ok";
}

(1)捕获范围

  • 只能捕获到ActionFilter执行前, 执行中, 执行后

  • 非常适合捕获发生在操作中的异常

  • 不像错误处理中间件那样灵活, 建议使用中间件处理异常

7、ResultFilterIAsyncResultFilter结果过滤器

  • 仅当操作或操作过滤器生成操作结果时, 才会执行结果过滤器, 不会在以下情况下执行结果过滤器

    • 授权*过滤器或资源过滤器使管道短路

    • 异常过滤器通过生成操作结果来处理异常

  • 如果在IResultFilter.OnResultExecuting中引发异常, 则会导致

    • 阻止操作结果和后续过滤器的执行

    • 结果被视为失败而不是成功

namespace FilterDemo.Filters
{
    public class CtmResultFilterAttribute : Attribute, IResultFilter
    {
        public void OnResultExecuted(ResultExecutedContext context)
        {
            //...
        }
​
        public void OnResultExecuting(ResultExecutingContext context)
        {
            //...
        }
    }
}

(1)IAlwaysRunResultFilterIAsyncAlwaysRunResultFilter

  • 这两个接口声明了一个针对所有操作结果运行IResultFilter实现, 不管发生什么异常都会正常运行

    • 不管资源过滤器、授权过滤器设置短路

    • 不挂异常过滤器执行

  • 应用: 如果在接口执行中出现异常, 控制器会返回状态码, 通过AOP根据不同状态码返回给前端

    public class CtmAlwaysRunResultFilterAttribute : Attribute, IAlwaysRunResultFilter
    {
    ​
        public void OnResultExecuting(ResultExecutingContext context)
        {
            //如果在接口执行中出现异常, 控制器会返回状态码
            if (context.Result is StatusCodeResult statusCodeResult
                && statusCodeResult.StatusCode == StatusCodes.Status404NotFound)
            {
                //根据不同状态码返回给前端
                context.Result = new ObjectResult("找不到资源") { StatusCode = StatusCodes.Status404NotFound };
            }
        }
    }

8、过滤器执行顺序

(1)未指定执行顺序

过滤器执行顺序如下全局注册 => 类注册 => 方法注册 =>

(2)program.cs中使用全局注册指定执行顺序

  • 数字越小, 执行顺序越靠前

builder.Services.AddControllers(opt => 
{ 
    opt.Filters.Add<CtmActionFilterEnv1Attribute>(1));
    opt.Filters.Add<CtmActionFilterEnv2Attribute>(2));
}

(3)Controller中使用特性的属性指定执行顺序

[TypeFilter(构造器, 参数, 可重复用, 执行顺序)]
public class Controller
{
    [TypeFilter(构造器, 参数, 可重复用, 执行顺序)]
    public string Test()
    {
        //...
    }
}
[HttpGet]
[TypeFilter(typeof(CtmExceptionFilterAttribute), Order = 1)]
public string TestExceptionFiltterAttribute() 
{
    throw new Exception("test");
    return "ok";
}

标签:string,builder,管道,context,new,过滤器,net6,public
From: https://www.cnblogs.com/phonk/p/17621837.html

相关文章

  • Servlet过滤器
    过滤器的基本概念Servlet过滤器从字面上的字意理解为经过一层次的过滤处理才达到使用的要求,而其实Servlet过滤器就是服务器与客户端请求与响应的中间层组件,在实际项目开发中Servlet过滤器主要用于对浏览器的请求进行过滤处理,将过滤后的请求再转给下一个资源。Filter是在......
  • 市政管道清淤疏通步骤
    市政管道清淤流程一般按照以下步骤进行:预先准备:清淤前需要进行相关准备工作。首先是了解管道的情况,包括管道的长度、直径、位置等信息。其次是准备所需的清淤工具和设备,如吊车、挖掘机、疏通器具、抽水泵等。管道排水:在清淤前,需要先将管道内的积水排掉。可以通过抽水泵将积水抽出,使......
  • django自定义过滤器
    https://docs.djangoproject.com/zh-hans/3.1/howto/custom-template-tags/代码布局自定义的tags和filters会保存在模块名为 templatetags 的目录内。模块文件的名字即稍候你用来加载tags的名字,所以小心不要采用一个可能与其它应用自定义的tags和filters冲突的名......
  • Docker(.Net6) 环境下使用 Haukcode.WkHtmlToPdfDotNet
     背景: 项目使用的是.Net6+Docker,需要将数据生成PDF保存到第三方文件存储服务器上。引用NuGet:Haukcode.WkHtmlToPdfDotNet 这个插件还是满好用的,支持Windows、Docker.可以直接通过Url转PDF,也可以通过Html字符,生成PDF.官方地址:https://github.com/Hak......
  • 授权过滤器—MVC中使用授权过滤器实现JWT权限认证
    一、什么是过滤器?过滤器定义:​过滤器与中间件很相似,过滤器(Filters)可在管道(pipeline)特定阶段(particularstage)前或后执行操作,可以将过滤器视为拦截器(interceptors)。在.NETMVC开发中,权限验证是非常重要的一部分。通过使用授权过滤器可以很方便地实现权限验证功能。这篇主要分......
  • 布隆过滤器
    布隆过滤器1.作用判断某一个值是否存在2.组成很长的二进制数组和一系列hash函数3.使用使用hash函数对该值进行hash运算,并将布隆过滤器中相应的位置设置为14.判断某一个数据在布隆过滤器中是否存在对该值使用布隆过滤器的一系列hash函数进行hash运算,然后判断对应的位置......
  • 老杜 JavaWeb 讲解(十九) ——Filter过滤器
    (十七)Filter过滤器Filter过滤器当前的OA项目存在什么缺陷?DeptServlet、EmpServlet、OrderServlet。每一个Servlet都是处理自己相关的业务。在这些Servlet执行之前都是需要判断用户是否登录了。如果用户登录了,可以继续操作,如果没有登录,需要用户登录。这段判断用户是否登录......
  • Redis Pipeline管道技术
    1.什么是pipelinePipeline是Redis提供的一种批量请求机制,可以在client端对多条命令进行打包,然后一次性发送给服务器,避免了多次网络往返的开销。2.pipeline的优势为了解释pipeline的作用,我们先思考一个问题:如果客户端需要依次执行多条Redis命令,该如何处理?客户端一次执行一条......
  • SpringBoot学习笔记--过滤器Filter
    一、普通过滤器Filter是Servlet规范中的过滤器,可以处理请求,对请求的参数、属性进行调整。常常在过滤器中处理字符编码。1、创建自定义过滤器类packagecom.cqjtu.vo;importjavax.servlet.*;importjava.io.IOException;//自定义过滤器类publicclassMyFilterimplements......
  • django开发的safe过滤器适用
    django_safe在自己开发的博客系统当中,如果使用django框架,那么在发布博客内容的时候,希望可以发布一些被渲染过的样式文本,比如说图片等。下面是发布文章用到的代码#views.pydefarticle_detail(request,username,article_id):user=UserInfo.objects.filter(username=use......