ibcadmin 发表于 2019-11-8 09:52:43

Asp.Net Core 轻松学-被低估的过滤器

</a>

<p style="font-size: 30px;">原创所在:<a href="https://www.cnblogs.com/viter/p/10107886.html">https://www.cnblogs.com/viter/p/10107886.html</a></p>
<p style="font-size: 30px;"><strong>目次</strong></p>
<ul>
<li><a href="#label_0">媒介</a></li>
<li><a href="#label_1">1. 介绍</a></li>
<li><a href="#label_2">3. 授权过滤器</a></li>
<li><a href="#label_3">4. 资源过滤器</a></li>
<li><a href="#label_4">5. 非常过滤器</a></li>
<li><a href="#label_5">6. 操纵过滤器 ActionFilterAttribute...</a></li>
<li><a href="#label_6">7.在过滤器中使用依赖注入</a></li>
<li><a href="#label_7">8. 过滤器的执行次序</a></li>
<li><a href="#label_8">竣事语</a></li>
<li><a href="#label_9">演示代码下载</a></li>
</ul>

<p> </p>
<h2 id="label_0">媒介</h2>
<p>过滤器,从我们开始开辟 Asp.Net 应用步伐开始,就不停陪伴在我们左右;Asp.Net Core 提供多种范例的过滤器,以满足多种多样的业务应用场景;而且在 Asp.Net Core 自己,过滤器的应用也非常广泛;但是,在现实的业务场景中,大部分开辟职员只使用到此中 1 到 2 种范例,固然,这此中大部分大概性是由于业务场景的实用性使然,本文实验简单介绍 Asp.Net Core 中提供的各种过滤器,以及现实的应用场景,渴望对您有所资助。</p>
<h2 id="label_1">1. 介绍</h2>
<a name="_label1_0"></a>
<h5 id="作用范围">1.1 作用范围</h5>
<blockquote>
<p>过滤器的作用范围<br />
每种差异的过滤器都有现实的作用范围,有一些全局过滤器还有作用域的限定,这取决于应用开辟者在界说和初始化过滤器的时间的选择,每个过滤器自己处理惩罚使命的权限和功能都大不相同,但是他们都有一个共同点,就是通过特性标记的方式使用,好比以下代码,对一个 Action 使用了过滤器 CustomerActionFilter</p>



</blockquote>
<code >      
      public ActionResult<string> Get(int id)
      {
            return "value";
      }</code>
<a name="_label1_1"></a>
<h5 id="过滤器的工作原理">1.2 过滤器的工作原理</h5>
<blockquote>
<p>原理表明<br />
过滤器一样寻常在 Asp.Net Core MVC 管道内运行,一样寻常在操纵执行之前(befor) 大概执行之后(after) 执行,以供开辟者可以选择在差异的执行阶段到场处理惩罚</p>



</blockquote>



<a name="_label1_2"></a>
<h5 id="过滤器范例看下图">1.3 过滤器范例,看下图</h5>
<p></p>
<blockquote>
<p>范例介绍<br />
上图既是 Asp.Net Core 内置的各种过滤器范例,也是其执行优先级次序,相同范例的过滤器还可以界说在某个阶段执行的次序</p>
<ol>
<li>授权过滤器 AuthorizeAttribute</li>
<li>资源过滤器 IResourceFilter</li>
<li>非常过滤器 IExceptionFilter</li>
<li>操纵过滤器 ActionFilterAttribute</li>
<li>效果过滤器 IResultFilter</li>



</ol>
</blockquote>
<h2 id="label_2">3. 授权过滤器</h2>
<p>3.1 使用介绍</p>
<blockquote>
<p>在哀求到达的时间开始执行,优先级最高,重要作用是提供用户哀求权限过滤,对不满足权限的用户,可以在过滤器内执行拒绝操纵,俗称“管道短路”<br />
*留意:该过滤器只有执行之前(befor),没有执行之后(after)的方法<br />
通常环境下,不须要自行编写过滤器,由于该过滤器在 Asp.Net Core 内部已经有了默认实现,我们须要做的就是设置授权战略大概实现自己的授权战略,然后由体系内置的授权过滤器调用授权战略即可<br />
必须将该过滤器内部大概出现的非常全部处理惩罚,由于在授权过滤器之前,没有任何组件可以大概捕获授权过滤器的非常,一旦授权管理器内部发生非常,该非常将直接输出到效果中</p>



</blockquote>
<p>3.2 应用场景</p>
<blockquote>
<p>授权管理器 AuthorizeAttribute 位于 定名空间 Microsoft.AspNetCore.Authorization 内,使用方式非常简单,检察以下代码</p>



</blockquote>
<code >   
    ")]
    public class UserController : Controller
    {
      
      
      public ActionResult<string> Get()
      {
            return "default";
      }

      
      public ActionResult<string> Post()
      {
            return "default";
      }
    }</code>
<blockquote>
<p>UserController 被应用了 Authorize 特性举行标记,体现对该控制器内的恣意操纵执行授权验证;但是单独对 Get 操纵举行了授权通过对标记,即 AllowAnonymous ,体现允许匿名访问<br />
这黑白经常用的做法,在授权应用中,经常须要对部分操纵举行单独的授权战略<br />
关于授权过滤器,先介绍到这里,下一篇单独对授权过滤器举行演示,由于关于这块的内容,要讲的着实是太多了</p>



</blockquote>
<h2 id="label_3">4. 资源过滤器</h2>
<blockquote>
<p>但哀求进入,通过授权过滤器后,接下来将执行资源过滤器(如果有界说),使用资源过滤器以致可以改变绑定模型,还可以在资源过滤器中实现缓存以进步性能</p>



</blockquote>
<p>4.1 资源管理器实现自接口 IResourceFilter 大概 IAsyncResourceFilter,现在我们来实现一个资源过滤器,输出一行信息,看看执行次序</p>
<code >    public class CustomerResourceFilter : Attribute, IResourceFilter
    {
      public void OnResourceExecuted(ResourceExecutedContext context)
      {
            Console.WriteLine("==== OnResourceExecuted");
      }

      public void OnResourceExecuting(ResourceExecutingContext context)
      {
            Console.WriteLine("==== OnResourceExecuting");
      }
    }</code>
<p>4.2 对 HomeController 的操纵应用该资源过滤器,看看对一个操纵同时应用 CustomerActionFilter 和 CustomerResourceFilter ,他们的执行次序是什么</p>
<code >    ")]
   
    public class HomeController : ControllerBase
    {
      
      
      
      public async Task<ActionResult<IEnumerable<string>>> Get()
      {
            return new string[] { "value1", "value2" };
      }
    }</code>
<p>4.3 启动步伐,访问 <ahref="http://localhost:5000/api/home,输出效果如下">http://localhost:5000/api/home,输出效果如下</a></p>
<p></p>
<blockquote>
<p>可以看到,执行次序和开篇的第一张图例同等,起首执行时资源过滤器的 OnResourceExecuting 方法,接着哀求接入了 操纵过滤器的 OnActionExecuting 方法,最后执行操纵过滤器的 OnResultExecuting 方法,然后把哀求交给资源过滤器的 OnResourceExecuted,最后返回到客户端<br />
以是,从执行次序可以看出,资源管理器的执行优先级总是高于操纵过滤器<br />
资源过滤器可以应用于控制器大概操纵,然后基于其执行优先级的特点,开辟员职员可以在资源过滤器中界说某些静态资源大概缓存直接将数据返回给客户端,并使其执行短路操纵,淘汰后续管道哀求步调,以进步服务器相应性能</p>



</blockquote>
<h2 id="label_4">5. 非常过滤器</h2>
<blockquote>
<p>在服务器向客户端写入相应内容之前,如果体系引发了非常,非常过滤器可以捕获该非常,该过滤器作用于全局范围,这也是最常用的过滤器</p>



</blockquote>
<p>5.1 创建一个非常过滤器</p>
<code >    public class CustomerExceptionFilter : Attribute, IExceptionFilter
    {
      public void OnException(ExceptionContext context)
      {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("发生了非常:{0}", context.Exception.Message);
            Console.ForegroundColor = ConsoleColor.Gray;
      }
    }</code>
<p>5.2 将 CustomerExceptionFilter 应用到 HomeController 上</p>
<blockquote>
<p>请留意,HomeController 上还同时应用了资源过滤器;现在要做到就是在资源过滤器内部抛出非常,看看 CustomerExceptionFilter 是否可以捕获该非常</p>
</blockquote>
<code >    public class CustomerResourceFilter : Attribute, IResourceFilter
    {
      public void OnResourceExecuted(ResourceExecutedContext context)
      {
            Console.WriteLine("==== OnResourceExecuted");
      }

      public void OnResourceExecuting(ResourceExecutingContext context)
      {
            Console.WriteLine("==== OnResourceExecuting");
            throw new Exception("资源管理器发生了非常");
      }
    }</code>
<p>5.3 运行步伐,访问 <ahref="http://localhost:5000/api/home">http://localhost:5000/api/home</a></p>
<p></p>
<blockquote>
<p>可以看到,体系抛出了非常;但是,非常过滤器 CustomerExceptionFilter 并没有捕获该非常,究竟证实资源过滤器的执行优先级还是高于非常过滤器,现在我们实验在操纵内部引发非常</p>
</blockquote>
<code >    ")]
   
   
   
    public class HomeController : ControllerBase
    {
      // GET api/values
      
      
      public async Task<ActionResult<IEnumerable<string>>> Get()
      {
            throw new Exception("Get操纵发生了非常");
            return new string[] { "value1", "value2" };
      }
    }</code>
<p>5.4 再次启动步伐,访问 <ahref="http://localhost:5000/api/home,控制台输出效果如下">http://localhost:5000/api/home,控制台输出效果如下</a></p>
<p></p>
<p>5.5 客户端得到了一个友爱的返回值</p>
<p></p>
<p>5.6 这是由于我们在非常过滤器内部将非常举行了出来,并通过设置 context.ExceptionHandled = true 来标记体现非常已经被处理惩罚,然后输出友爱信息</p>
<code >    public class CustomerExceptionFilter : Attribute, IExceptionFilter
    {
      public void OnException(ExceptionContext context)
      {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("发生了非常:{0}", context.Exception.Message);
            Console.ForegroundColor = ConsoleColor.Gray;

            context.Result = new JsonResult(new { code = 500, message = context.Exception.Message });
            context.ExceptionHandled = true;
      }
    }</code>
<blockquote>
<p>非常过滤器的应用非常简单,你可以在其内部将非常写入日记,大概执行别的须要处理惩罚的逻辑</p>
</blockquote>
<h2 id="label_5">6. 操纵过滤器 ActionFilterAttribute 和 效果过滤器 IResultFilter</h2>
<blockquote><ol>
<li>操纵过滤器:当哀求进入 API 接口的时间,操纵过滤器提供了一个进入之前(before)和进入之后(after)到场功能,可以使用该过滤器对进入 API 的参数和效果举行干预</li>
<li>效果过滤器:这个过滤器的作用和操纵过滤器非常相似,重要其作用范围是有微小区别的,效果过滤器是在操纵即将返回效果到客户端之前(before)大概之后(after)执行干预,好比你可以在返回效果之后(after)去渲染视图</li>
</ol></blockquote>
<p>6.1 之以是将这两个过滤器放在一起讲,是由于,这两个过滤器就像一对孪生兄弟一样,正所谓善始善终,起首来看操纵过滤器</p>
<code >   
    public abstract class ActionFilterAttribute : Attribute, IActionFilter, IFilterMetadata, IAsyncActionFilter, IResultFilter, IAsyncResultFilter, IOrderedFilter
    {
      protected ActionFilterAttribute();

      //
      public int Order { get; set; }

      //
      public virtual void OnActionExecuted(ActionExecutedContext context);
      //
      public virtual void OnActionExecuting(ActionExecutingContext context);
      //
      
      public virtual Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next);
      //
      public virtual void OnResultExecuted(ResultExecutedContext context);
      //
      public virtual void OnResultExecuting(ResultExecutingContext context);
      //
      
      public virtual Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next);
    }
</code>
<blockquote>
<p>操纵过滤器包罗 6 个根本方法,分别是执行前(before)执行后(after),写入效果前(before)写入后(after)<br />
为什么会如许呢,由于操纵过滤器实现的接口中包罗了效果过滤器的接口<br />
根据官方的提示,如果你须要重写 ActionFilterAttribute 的方法以处理惩罚自界说的业务逻辑,那么 OnActionExecutionAsync 这个异步方法不应该和 执行前(before)执行后(after)同时共存<br />
同样,写入效果前(before)写入后(after)和 OnResultExecutionAsync 也是一样</p>



</blockquote>
<p>6.2 操纵过滤器包罗了 写入效果前(before)写入后(after)的方法,这使得我们可以不消去界说效果过滤器就可以实现对写入效果的管理</p>
<blockquote>
<p>固然,最好的做法是界说效果过滤器,这有助于业务分类,且逻辑清晰明了,但是如果你渴望可以使用异步操纵,很遗憾,效果过滤器不支持该方法</p>



</blockquote>
<p>6.3 下面来看效果过滤的界说</p>
<code >    public class CustomerResultFilter : Attribute, IResultFilter
    {
      public void OnResultExecuted(ResultExecutedContext context)
      {
            Console.WriteLine("OnResultExecuted");
      }

      public void OnResultExecuting(ResultExecutingContext context)
      {
            Console.WriteLine("OnResultExecuting");
      }
    }</code>
<blockquote>
<p>代码非常简单,就是实现接口 IResultFilter<br />
IResultFilter 的工作原理和操纵过滤器的写入效果前(before)写入后(after)的方法执行同等,可以看到,他们两个方法和参数名称都是同等的,由于他们都是实现同一个接口 IResultFilter</p>



</blockquote>
<p>6.4 使用效果过滤器实现对输出效果的干预</p>
<blockquote>
<p>下面就简单在效果过滤器内部去对已经构造好的数据举行干预,HomeController.Get 方法本应该输出 一个数组,我们在Header 中增长一项输出:Author=From Ron.liang</p>



</blockquote>
<code >    public class CustomerResultFilter : Attribute, IResultFilter
    {
      public void OnResultExecuted(ResultExecutedContext context)
      {
            // ToDo
      }

      public void OnResultExecuting(ResultExecutingContext context)
      {
            // 干预效果
            context.HttpContext.Response.Headers.Add("Author", "From Ron.liang");
      }
    }</code>
<p>6.5 输出效果</p>
<p></p>
<h2 id="label_6">7.在过滤器中使用依赖注入</h2>
<blockquote>
<p>在上面介绍的各种各样的过滤器中,偶然间我们大概须要读取步伐运行环境的信息,根据差异的环境做出差异的相应内容<br />
好比,上面的效果过滤器写入作者信息,大概我们只渴望在开辟环境输出,而在产物环境忽略</p>



</blockquote>
<p>7.1 使用 GetService,以支持依赖注入</p>
<code >      public void OnResultExecuting(ResultExecutingContext context)
      {
            var env = (IHostingEnvironment)context.HttpContext.RequestServices.GetService(typeof(IHostingEnvironment));

            Console.ForegroundColor = ConsoleColor.Blue;
            Console.WriteLine("OnResultExecuting,{0}", env.EnvironmentName);
            Console.ForegroundColor = ConsoleColor.Gray;

            // 干预效果
            if (env.IsDevelopment())
                context.HttpContext.Response.Headers.Add("Author", "From Ron.liang");
      }</code>
<blockquote>
<p>上面的从 context.HttpContext.RequestServices.GetService(typeof(IHostingEnvironment)) 获取了环境变量,并判定在开辟环境下为相应头添加内容</p>
</blockquote>
<p>7.2 在过滤器中使用中央件</p>
<blockquote>
<p>Asp.Net Core 提供了一个功能,使得我们在过滤器中可以使用中央件,现实上,这两者的使用方式非常类似<br />
如果你渴望这么做,可以界说一个包罗 Configure(IApplicationBuilder applicationBuilder) 方法的类,在控制器大概操纵中使用它</p>



</blockquote>
<p>7.3 界说注册管理管道类</p>
<code >    public class RegisterManagerPipeline
    {
      public void Configure(IApplicationBuilder applicationBuilder)
      {
            CookieAuthenticationOptions options = new CookieAuthenticationOptions();

            applicationBuilder.UseCors(config =>
            {
                config.AllowAnyOrigin();
            });
      }
    }</code>
<blockquote>
<p>RegisterManagerPipeline 界说了一个 Configure 方法,在该方法内部执行一个跨域设置,体现允许任何泉源访问该站点;然后,我们在 UserController 中应用该管道</p>
</blockquote>
<code >   
    ")]
   
    public class UserController : Controller
    {
      // GET: api/<controller>
      
      
      public ActionResult<string> Get()
      {
            return "default";
      }
    }</code>
<blockquote>
<p>应用方式非常简单,就像使用平凡过滤器一样对控制器举行特性标记即可<br />
所差异的是,这里使用的是 MiddlewareFilter 举行注册 RegisterManagerPipeline<br />
管道式过滤器的优先级非常高,以致比授权过滤器的优先级还高,在使用的时间须要特殊留意应用场景</p>



</blockquote>
<h2 id="label_7">8. 过滤器的执行次序</h2>
<blockquote>
<p>相同范例的过滤器其执行次序可以使用 Order 字段举行指定,该值为一个 int32 范例,值越小体现优先级越高,该值只能作用于相同范例的过滤器<br />
好比,界说了两个 ActionFilter ,UserNameActionFilter,UserAgeActionFilter,分别订定其 Order 字段值为 10,5,那么 UserAgeActionFilter 将会在调用 ,UserNameActionFilter 之前执行<br />
但是,纵然指定了 Order ,ActionFilter 的执行优先级也不会逾越授权管理器 AuthorizeAttribute,这是操持上的差异</p>



</blockquote>
<p>8.1 Order 演示代码</p>
<code >      
      
      
      public void Post( UserModel value)
      {
      }</code>
<p>8.2 输出效果</p>
<p></p>
<blockquote>
<p>上图输出的黄色部分笔墨清晰的阐明了过滤器的执行次序<br />
体现执行了资源过滤器,接着执行了 Order=5 的 UserAgeActionFilter ,最后执行了 Order=10 的 UserNameActionFilter 过滤器<br />
可以看到,固然操纵过滤器设置了 Order=5,但其执行优先级仍旧不能逾越授权过滤器,以致无法逾越资源过滤器</p>



</blockquote>
<h2 id="label_8">竣事语</h2>
<ul>
<li>
<p>本文简单介绍了 Asp.Net Core 下体系内置的各种各样的过滤器,分别是</p>
<blockquote><ol>
<li>授权过滤器 AuthorizeAttribute</li>
<li>资源过滤器 IResourceFilter</li>
<li>非常过滤器 IExceptionFilter</li>
<li>操纵过滤器 ActionFilterAttribute</li>
<li>效果过滤器 IResultFilter</li>



</ol>
</blockquote>


</li>
<li>还通过一些简单说实例演示了过滤器的执行过程</li>
<li>
<p>最后介绍了怎样在过滤器中使用中央件,以及对过滤器的执行次序举行了具体的演示</p>


</li>



</ul>
<h2 id="label_9">演示代码下载</h2>
<p><ahref="https://github.com/lianggx/EasyAspNetCoreDemo/tree/master/Ron.FilterDemo">https://github.com/lianggx/EasyAspNetCoreDemo/tree/master/Ron.FilterDemo</a></p>

kericnnoe1964 发表于 2022-12-13 02:28:24

6666666666666
页: [1]
查看完整版本: Asp.Net Core 轻松学-被低估的过滤器