• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Asp.NetCore进阶(四)——过滤器Filters

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

一、介绍  

  Asp.Net Core Filter 使得可以在请求处理管道的特定阶段的前后执行代码,我们

  Asp.Net Core 提供了5中过滤器类型,分别是:

Authorization filters,授权过滤器是最先执行并且决定请求用户是否经过授权认证,如果请求未获授权,授权过滤器可以让管道短路。

Resource filters,资源过滤器在Authorization filter执行完后执行,它有两个方法,OnResourceExecuting可以在filter管道的其余阶段之前运行代码,例如可以在模型绑定之前运行。OnResourceExecuted则可以在filter管道的其余阶段之后运行代码

不可以在 Razor Pages 中使用。

Exception filters,异常过滤器常常被用于在向响应正文写入任何内容之前,对未经处理的异常应用全局策略。

成功执行时,它才会运行。

  至于它们在filter管道中的交互方式可以看下图。

二、实现

  Asp.Net Core提供了同步和异步两种filter接口定义,通过实现不同的接口可以实现同步或异步的过滤器,但是如果一个类同时实现了同步和异步的接口,Asp.Net Core的filter 管道只会调用异步的方法,即异步优先。具体的做法可以参考微软官方文档 https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2

  以下是IActionFilter和IAsyncActionFilter的实现

public class MySampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

public class SampleAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context,
        ActionExecutionDelegate next)
    {
        // Do something before the action executes.

        // next() calls the action method.
        var resultContext = await next();
        // resultContext.Result is set.
        // Do something after the action executes.
    }
}

  除了直接实现Filter接口,Asp.Net Core同时提供了一些基于特性的过滤器,我们可以继承相应的特性来实现自定义的过滤器。这些特性包括ActionFilterAttributeExceptionFilterAttributeResultFilterAttributeFormatFilterAttributeServiceFilterAttributeTypeFilterAttribute

  下面是微软文档的一个例子,在Result Filter给响应添加Header。

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);
    }
}

[AddHeader("Author", "Joe Smith")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header is set.");
    }
}

 

三、Filter 的作用域和执行顺序

  可以将我们自定义的filter添加到我们的代码中进行调用,添加的方式有三种,对应其三个作用域:在Action上添加特性、在Controller上添加特性和添加全局filter。

  下面先来看下添加全局filter的做法,我们知道,在Asp.Net MVC中,filter都是在控制器实例化之后才能生效的,其Filter应该是确定的,不能被注入参数,但是到了Asp.Net Core,全局注册是在ConfigureServices方法中完成的,它可以被注入参数。

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });


    services.AddMvc(options =>
    {
        options.Filters.Add(new AddHeaderAttribute("name", "Jesen"));
        options.Filters.Add(typeof(AddLogActionAttribute));
        options.Filters.Add(typeof(ExceptionHandlerAttribute));
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

  上述代码在addMvc中将我们自定义的 filter 添加到全局当中,这样对所有的控制器和action都产生了作用。而其他两种作用域的用法就是直接将特性添加到其上面。

[AddHeader("", "")]
public class HomeController : Controller
{
    [AddLogAction]
    public IActionResult Index()
    {
        return View();
    }
}

  那么这三种作用域的默认执行顺序是怎样的,下图很好的进行了展示。

  那么我们是否可以改变其默认执行顺序呢,答案当然是肯定的。我们在具有较高的 Order 值的 filter 之后运行 after 代码。具体做法可以在使用构造函数参数时设置Order 属性。

[CustomFilter(Name = "Controller Level Attribute", Order=1)]

  在改变了Order属性后的执行顺序如下图所示

 四、依赖注入

  在前面添加filter事,我们在全局添加方式中知道了 filter 可以通过类型添加,也可以通过实例添加,通过实例添加,则该实例会被应用于所有的请求,按照类型添加则将激活该类型,这意味着它会为每个请求创建一个实例,依赖注入将注入所有构造函数的依赖项。

  但是如果通过特性添加到Controller或者Action上时,该 filter 不能由依赖注入提供构造函数依赖项,这是因为特性在添加时必须提供它的构造函数参数,这是由于特性的原理决定的。那是不是通过Controller或Action的特性不能有构造函数参数呢,肯定不是的,可以通过以下特性来获得依赖注入:ServiceFilterAttribute、TypeFilterAttribute和在特性上实现 IFilterFactory。

namespace FilterDemo.Filter
{
    public class AddLogActionAttribute : ActionFilterAttribute
    {
        private readonly ILogger _logger;

        public AddLogActionAttribute(ILoggerFactory loggerFactory)
        {
            this._logger = loggerFactory.CreateLogger<AddLogActionAttribute>();
        }

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            string controllerName = (string)context.RouteData.Values["controller"];
            string actionName = (string)context.RouteData.Values["action"];

            this._logger.LogInformation($"{controllerName}的{actionName}开始执行了...")
            base.OnActionExecuting(context);
        }


        public override void OnActionExecuted(ActionExecutedContext context)
        {
            base.OnActionExecuted(context);
        }
    }
}

  上述代码我们定义了带Logger参数的AddLogActionAttribute Filter,接下来实现怎么在Controller或Action上使用,首先在Startup中添加注入

services.AddScoped<AddLogActionAttribute>();

  然后在Controller或Action上使用 ServiceFilter

[ServiceFilter(typeof(AddLogActionAttribute))]
public class HomeController : Controller

  TypeFilterAttribute与ServiceFilterAttribute类似,使用 TypeFilterAttribute 引用的类型不需要注册在 DI 容器中。

 [TypeFilter(typeof(AddLogActionAttribute))]
 public IActionResult Index()
 {
      return View();
 }

五、Resource Filter  

  最后,我们来看一下Asp.Net Core不同于之前Asp.Net MVC的 ResourceFilter,在上述介绍中,我们知道了Resource Filter在Authorization Filter执行之后执行,然后才会去实例化控制器,那么Resource Filter 就比较适合用来做缓存,接下来我们自定义一个Resource Filter。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace FilterDemo.Filter
{
    public class CacheResourceFilterAttribute : Attribute, IResourceFilter
    {
        private static readonly Dictionary<string, object> _Cache = new Dictionary<string, object>();
        private string _cacheKey;
        /// <summary>
        /// 控制器实例化之前
        /// </summary>
        /// <param name="context"></param>
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            _cacheKey = context.HttpContext.Request.Path.ToString();
            if (_Cache.ContainsKey(_cacheKey))
            {
                var cachedValue = _Cache[_cacheKey] as ViewResult;
                if (cachedValue != null)
                {
                    context.Result = cachedValue; //设置该Result将是filter管道短路,阻止执行管道的其他阶段
                }
            }
        }

        /// <summary>
        /// 把请求都处理完后执行
        /// </summary>
        /// <param name="context"></param>
        public void OnResourceExecuted(ResourceExecutedContext context)
        {
            if (!String.IsNullOrEmpty(_cacheKey) &&
                !_Cache.ContainsKey(_cacheKey))
            {
                var result = context.Result as ViewResult;
                if (result != null)
                {
                    _Cache.Add(_cacheKey, result);
                }
            }
        }
    }
}

  将其添加到HomeController的Index上

[CacheResourceFilter]
[TypeFilter(typeof(AddLogActionAttribute))]
public IActionResult Index()
{
    return View();
}

  运行可以发现第一次请求按照默认顺序执行,第二次请求会在Cache中查找该请求路径是否已经在Cache当中,存在则直接返回到Result,中断了请求进入管道的其他阶段。


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
ASP.NETMVC系列框架搭建(一)之仓储层的搭建发布时间:2022-07-10
下一篇:
ASP.NET中form提交时isPostBack的问题发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap