zoukankan      html  css  js  c++  java
  • asp.net core系列 68 Filter管道过滤器

    一.概述

      本篇详细了解一下asp.net core filters,filter叫"筛选器"也叫"过滤器",是请求处理管道中的特定阶段之前或之后运行代码。filter用于处理横切关注点。 横切关注点的示例包括:错误处理、缓存、配置、授权和日志记录。 filter可以避免重复代码,通过Attribute特性来实现filter过滤。Filter适应于 Razor Pages,  API controllers,  mvc controllers。filter基类是IFilterMetadata 接口,该接口只是用来标记是一个filter过滤器。

      前段时间在项目中实现了IAsyncAuthorizationFilter接口对用户访问controller或action进行了授权,在OnAuthorizationAsync方法中使用context.Result可使管道短道。IAsyncAuthorizationFilter是属于授权过滤器中的一种。勿在授权过滤器内抛出异常,这是因为所抛出的异常不会被处理。下面是简要的实现授权代码:

       [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
        public class PermissionFilter : Attribute,IAsyncAuthorizationFilter
      {
        public   Task OnAuthorizationAsync(AuthorizationFilterContext context)
            {
                    IIdentity user = context.HttpContext.User.Identity;
                    if (!user.IsAuthenticated)
                    {
                //跳转到登录页
                  context.Result = new LocalRedirectResult(url);
                 return Task.CompletedTask;
                 }
     
             //根据当前用户,判断当前访问的action,没有权限时返回403错误
             context.Result = new ForbidResult();
                
                 return Task.CompletedTask;
         }
        }

      在官方文档中说到:"自定义授权筛选器Filter需要自定义授权框架, 建议配置授权策略或编写自定义授权策略,而不是编写自定义Filter筛选器"。

      这里的意思是说,如果在项目中不使用asp.net core自带的identity,那么需要自定义授权框架,也就是需要自己建立一套用户权限表。 如果使用了identity建议配置授权策略或编写自定义授权策略。如果不用identity自己建立用户权限表,那么就可以编写自定义Filter筛选器。我在项目开发中是自己建立了用户权限表没有用identity。因为使用identity表还需要扩展字段,而且与EF core结合紧密,如果不使用EF core需要重写访问identity表的实现。

      下面是identity与ef core相关的代码: 

       services.AddIdentity<ApplicationUser, IdentityRole>()
                    .AddEntityFrameworkStores<ApplicationDbContext>()
                    .AddDefaultTokenProviders();
    
       -- TContext必须是DbContext类型
        public static IdentityBuilder AddEntityFrameworkStores<TContext>(this IdentityBuilder builder) where TContext : DbContext

    二.Filter 筛选器类型

        Authorization filters  授权筛选器

        Resource filters       资源筛选器

        Action filters            操作筛选器 (Razor Pages 中使用 IPageFilter 和 IAsyncPageFilter)

        Exception filters      异常筛选器

        Result filters            结果筛选器

        每种Filter 类型都在Filter 管道中的不同阶段执行,当用户请求进来时,经过Filter管道,执行Filter的阶段顺序如下所示:

        上面每种Filter 类型都有自己的接口,都同时支持同步和异步实现,上面的授权示例就是一个异步实现授权筛选器IAsyncAuthorizationFilter。Filter 接口或直接或间接实现了IFilterMetadata接口,IFilterMetadata接口只是用来标记是一个Filter 。

    三.filter筛选器作用域

      (1) 将 attribute特性应用在 action上。

      (2) 将 attribute特性应用在 controller上。

      (3) 全局筛选器应用在controller和action上

        下面使用全局筛选器,使用MvcOptions.Filters 集合,添加三个筛选器,如下面的代码所示:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc(options =>
        {
            options.Filters.Add(new AddHeaderAttribute("GlobalAddHeader",
                "Result filter added to MvcOptions.Filters"));         // An instance
            options.Filters.Add(typeof(MySampleActionFilter));         // By type
            options.Filters.Add(new SampleGlobalActionFilter());       // An instance
        }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }
    //实现了内置筛选器属性
    public class AddHeaderAttribute : ResultFilterAttribute
    //实现了操作筛选器
    public class MySampleActionFilter : IActionFilter
    //实现了操作筛选器
    public class SampleGlobalActionFilter : IActionFilter

    四.内置筛选器Attribute属性

        下面都是filter内置的属性类,需要去实现这些抽象属性类。

          ActionFilterAttribute
          ExceptionFilterAttribute
          ResultFilterAttribute
          FormatFilterAttribute
          ServiceFilterAttribute
          TypeFilterAttribute

      下面一个示例是为响应添加标头,开发人员来实现ResultFilterAttribute抽象类:

    public class AddHeaderAttribute : ResultFilterAttribute
    {
        private readonly string _name;
        private readonly string _value;
    
        public AddHeaderAttribute(string name, string value)
        {
            _name = name;
            _value = value;
        }
    
        public override void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add( _name, new string[] { _value });
            base.OnResultExecuting(context);
        }
    }

         通过使用属性,筛选器可接收参数,将 AddHeaderAttribute 添加到控制器或操作方法,并指定 HTTP 标头的名称和值,如下所示:

            [AddHeader("Author", "Steve Smith @ardalis")]
            public IActionResult Hello(string name)
            {
                return Content($"Hello {name}");
            }
        

    五.Filter三种依赖关系注入

        (1)ServiceFilterAttribute
        (2)TypeFilterAttribute
        (3)在属性上实现 IFilterFactory。

      5.1 ServiceFilterAttribute演示

        下面是在 ConfigureServices 中注册服务筛选器实现类型,内置的ServiceFilterAttribute会从DI 检索筛选器实例。

    public class AddHeaderResultServiceFilter : IResultFilter
    {
        private ILogger _logger;
        public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
        }
    
        public void OnResultExecuting(ResultExecutingContext context)
        {
            var headerName = "OnResultExecuting";
            context.HttpContext.Response.Headers.Add(
                headerName, new string[] { "ResultExecutingSuccessfully" });
            _logger.LogInformation($"Header added: {headerName}");
        }
    
        public void OnResultExecuted(ResultExecutedContext context)
        {
            // Can't add to headers here because response has started.
        }
    }

         在以下代码中,AddHeaderResultServiceFilter 将添加到 DI 容器中:

        services.AddScoped<AddHeaderResultServiceFilter>();

        在以下代码中,通过ServiceFilter 属性,将从 DI 中检索 AddHeaderResultServiceFilter 筛选器的实例:

    [ServiceFilter(typeof(AddHeaderResultServiceFilter))]
    public IActionResult Index()
    {
        return View();
    }

    六. 各种筛选器介绍

      6.1 Authorization filters 

        是筛选器管道中第一个运行的筛选器。是控制对Action方法的访问。

         在Action之前执行的方法,没有在Action之后执行的方法。

           注意:不要在授权筛选器中引发异常

        场景:

          如果使用identity,可用配置授权策略或编写自定义授权策略。详情查看identity部分。

          如果不使用identity,可编写自定义筛选器。

      6.2 Resource filters

        实现 IResourceFilter 或 IAsyncResourceFilter 接口。

        它执行会覆盖筛选器管道的绝大部分。

        它在授权筛选器之后运行。

        场景:

          可以防止模型绑定访问表单数据。

          用于上传大型文件,以防止表单数据被读入内存。

      6.3 Action filters

        实现 IActionFilter 或 IAsyncActionFilter 接口。

        它围绕着Action方法的执行。

        它方法中有个重要属性ActionArguments ,用于读取action的输入参数。

        场景:

          可以在该接口的OnActionExecuting方法中进行验证模型状态(ModelState.IsValid),如果状态无效,则返回错误(这个场景很有用)。

      6.4 Exception filters

        实现 IExceptionFilter 或 IAsyncExceptionFilter。

        它没有之前和之后的事件,

        可实现该接口的 OnException 或 OnExceptionAsync方法。

        处理 Razor 页面或控制器创建、模型绑定、操作筛选器或操作方法中发生的未经处理的异常

        若要处理异常(不再throw),请将 ExceptionHandled 属性设置为 true。

        场景:

          实现常见的错误处理策略。

          非常适合捕获发生在action中的异常。

        下面是项目中的一个示例, 注意在授权器中引发的异常是无法捕获的。 

        public class GlobalAsyncExceptionFilter : IAsyncExceptionFilter
        {
            //日志对象
            public readonly ILoggerEX _logger;
    
            public GlobalAsyncExceptionFilter(ILoggerEX logger)
            {
                _logger = logger;
            }
    
            public Task OnExceptionAsync(ExceptionContext context)
            {
                string errormsg = "全局捕获未处理的异常,errorMsg:" + context.Exception.Message + context.Exception.StackTrace;
                _logger.Error(errormsg);
                context.Result = new ObjectResult(errormsg);
           context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
    //不再throw,在此捕获 context.ExceptionHandled = true; return Task.CompletedTask; } }
                services.AddMvc(option=> {
                    option.Filters.Add(typeof(GlobalAsyncExceptionFilter));
                }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            public CustomerController(INotificationHandler<DomainNotification> notifications,
                                      ILoggerEX logger,
                                      CustomerAppService customerAppService)
                : base(notifications)
            {
                _customerAppService = customerAppService;
                _logger = logger;
                throw new Exception("自定义一个未捕获的异常!");
            }

        当访问Customer控制器时,全局异常过滤器将会自动捕获,如下所示:

      6.5 Result filters

        实现 IResultFilter 或 IAsyncResultFilter 或 IAlwaysRunResultFilter 或 IAsyncAlwaysRunResultFilter

        它执行围绕着action结果的执行。当异常筛选器处理异常时,不执行结果筛选器

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

          (1) 阻止action结果和后续筛选器的执行。

          (2) 结果被视为失败。

      更详细的资料参考官网文档,后续在实际项目中应用到的筛选器,将会再本篇继续补上。

      参考资料:

        官方文档

        官方示例代码

  • 相关阅读:
    编程之美 2.3寻找发帖‘水王’ 扩展问题
    编程之美:1.12 扩展问题 解答与思考
    编程之美:1.9高效率安排见面会 图的m着色问题 回溯法
    研究生毕业课题怎么确定(转)
    图模型的统计推断 inference in graphical models(马尔科夫链的推断)
    微信js-sdk注意事项
    bootstrap-material-design-个人总结
    前端页面优化
    Material Design
    马克飞象
  • 原文地址:https://www.cnblogs.com/MrHSR/p/11184801.html
Copyright © 2011-2022 走看看