一.概述

  本篇详细了解一下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. datacontract helper

    public static class DataContractHelper { public static void ToDCFile<T>(this T obj, string pat ...

  2. Java发展历程

    Java 的发展要追溯到 1991 年,Patrick Naughton(帕特里克·诺顿)和 James Gosling(詹姆斯·高斯林)带领 Sun 公司的工程师打算为有线电视转换盒之类的消费产品设 ...

  3. 算法之--回溯法-迷宫问题【python实现】

    题目描述 定义一个二维数组N*M(其中2<=N<=10;2<=M<=10),如5 × 5数组下所示: int maze[5][5] = { 0, 1, 0, 0, 0, 0,  ...

  4. LINE_NO in format of XXXX example -> Line 10 is 0010

    select case when length(line_no) = 1 then to_char(line_no) when length(line_no) = 2 then '00' || lin ...

  5. C#判断是否相等

    判断对象是否相等,因为平时用的一般都是int.bool.string类型的数据是否相等. 同时也是只判断它们的“值”是否相等.于是都是用“==”或是Equal()方法来判断. 但这并不能判断出是否为同 ...

  6. 中芯国际在CSTIC上悉数追赶国际先进水平的布局

    作为中国最大.工艺最先进的晶圆厂,中芯国际的一举一动都会受到大家的关注.在由SEMI主办的2017’中国国际半导体技术大会(CSTIC 2017)上,中芯国际CEO邱慈云博士给我们带来了最新的介绍. ...

  7. QThread的源码(直接搜索"thread.cpp"即可,或者在github里搜)

    void QThread::run() { (void) exec(); } int QThread::exec() { Q_D(QThread); QMutexLocker locker(& ...

  8. 设置tablewidget自适应列宽和设置自动等宽

      在网上很容易知道自适应列宽,100%不留空显示,这里还是提下: /*设置表格是否充满,即行末不留空*/ ui->tableWidget->horizontalHeader()-> ...

  9. 【canvas】基础练习二 文字

    demo1 fillText strokeText绘制文字 <!DOCTYPE html> <html lang="en"> <head> &l ...

  10. 模拟键盘发送文字(使用SendInput API函数)

    嗯...老生常谈的话题, 不过系统的总结了一下, 找了个相对简单的实现方式, 可以方便的发送任何文字 参考另一片文章: http://www.cnblogs.com/-clq/archive/2011 ...