一.概述

  本篇详细了解一下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) 结果被视为失败。

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

  参考资料:

    官方文档

    官方示例代码

asp.net core系列 68 Filter管道过滤器的更多相关文章

  1. 【目录】asp.net core系列篇

    随笔分类 - asp.net core系列篇 asp.net core系列 68 Filter管道过滤器 摘要: 一.概述 本篇详细了解一下asp.net core filters,filter叫&q ...

  2. Ajax跨域问题及解决方案 asp.net core 系列之允许跨越访问(Enable Cross-Origin Requests:CORS) c#中的Cache缓存技术 C#中的Cookie C#串口扫描枪的简单实现 c#Socket服务器与客户端的开发(2)

    Ajax跨域问题及解决方案   目录 复现Ajax跨域问题 Ajax跨域介绍 Ajax跨域解决方案 一. 在服务端添加响应头Access-Control-Allow-Origin 二. 使用JSONP ...

  3. asp.net core 系列 8 Razor框架路由(下)

    三.页面路由操作约定 接着上篇讲asp.net core 系列 7 Razor框架路由.在上篇继续第三节 "页面路由操作约定" 的最后一小节 AddPageRoute . 3.3. ...

  4. WPF中的常用布局 栈的实现 一个关于素数的神奇性质 C# defualt关键字默认值用法 接口通俗理解 C# Json序列化和反序列化 ASP.NET CORE系列【五】webapi整理以及RESTful风格化

    WPF中的常用布局   一 写在开头1.1 写在开头微软是一家伟大的公司.评价一门技术的好坏得看具体的需求,没有哪门技术是面面俱到地好,应该抛弃对微软和微软的技术的偏见. 1.2 本文内容本文主要内容 ...

  5. Asp.net Core 系列之--2.ORM初探:Dapper实现MySql数据库各类操作

    ChuanGoing 2019-09-10 距离上一篇近一个月时间,断断续续才把本篇码完,后面将加快进度,争取年度内把本系列基本介绍完成,同时督促本人持续学习. 本篇学习曲线: 1.初识Dapper ...

  6. 【asp.net core 系列】10 实战之ActionFilter

    0.前言 在上一篇中,我们提到了如何创建一个UnitOfWork并通过ActionFilter设置启用.这一篇我们将简单介绍一下ActionFilter以及如何利用ActionFilter,顺便补齐一 ...

  7. 1.1专题介绍「深入浅出ASP.NET Core系列」

    大家好,我是IT人张飞洪,专注于.NET平台十年有余. 工作之余喜欢阅读和写作,学习的内容包括数据结构/算法.网络技术.Linux系统原理.数据库技术原理,设计模式.前沿架构.微服务.容器技术等等…… ...

  8. asp.net core系列 30 EF管理数据库架构--必备知识 迁移

    一.管理数据库架构概述 EF Core 提供两种主要方法来保持 EF Core 模型和数据库架构同步.一是以 EF Core 模型为基准,二是以数据库为基准. (1)如果希望以 EF Core 模型为 ...

  9. asp.net core系列 40 Web 应用MVC 介绍与详细示例

    一. MVC介绍 MVC架构模式有助于实现关注点分离.视图和控制器均依赖于模型. 但是,模型既不依赖于视图,也不依赖于控制器. 这是分离的一个关键优势. 这种分离允许模型独立于可视化展示进行构建和测试 ...

随机推荐

  1. Win8 Metro(C#)数字图像处理--2.56简单统计法图像二值化

    原文:Win8 Metro(C#)数字图像处理--2.56简单统计法图像二值化  [函数名称] 简单统计法图像二值化 WriteableBitmap StatisticalThSegment(Wr ...

  2. MEF、MAF和Unity的区别

    在刚学习MEF时,经常会问一个问题,那就是MEF和MAF这样的插件框架.和Unity这样的IoC框架到底有什么区别.MEF与MAF(Managed Addin Framework)最大不同在于:前者关 ...

  3. winform picturebox设置布局样式

    这里分背景图和直接显示的图片的布局 背景图的布局为BackgroundImageLayout设置为strecth即为自动拉伸 图片的布局为SizeMode 设置为StretchImage即自动拉伸

  4. ML:吴恩达 机器学习 课程笔记(Week1~2)

    吴恩达(Andrew Ng)机器学习课程:课程主页 由于博客编辑器有些不顺手,所有的课程笔记将全部以手写照片形式上传.有机会将在之后上传课程中各个ML算法实现的Octave版本. Linear Reg ...

  5. 转换GMT秒数为日期时间格式-Delphi源码

    转换GMT秒数为日期时间格式-Delphi源码.收藏最近在写PE分析工具的时候,需要转换TimeDateStamp字段值为日期时间格式,这是Delphi的源码. //把GMT时间的秒数转换成日期时间格 ...

  6. delphi如何输出当前堆栈

    想实现,输出当前运行的堆栈,有会的吗?给点思路 方法很多,参考: https://bitbucket.org/shadow_cs/delphi-leakcheck/ 的 https://bitbuck ...

  7. ring3下利用WMI监视进程创建(vc版)

    #include "stdafx.h" #define _WIN32_DCOM #include <iostream> using namespace std; #in ...

  8. Codility---FrogJmp

    Task description A small frog wants to get to the other side of the road. The frog is currently loca ...

  9. python连接数据库(1)——mysql

    mysql是世界上应用最广的免费数据库,python当然也提供了对它的调用. 首先pip install pymysql,当然自己要知道数据库的用户名和密码,本地数据库的host就是localhost ...

  10. Mount挂载/data时出现mount: /data is busy 如何解决?

    1.df -h查看下挂载点/data是否正在使用,有时候会存在挂载了,但df -h不会显示出来,这时候 grep “/data” /proc/mounts 来进行查看 2.当确认挂载点/data正在使 ...