zoukankan      html  css  js  c++  java
  • .NET Core开发日志——Filter

    ASP.NET Core MVC中的Filter作用是在请求处理管道的某些阶段之前或之后可以运行特定的代码。

    Filter特性在之前的ASP.NET MVC中已经出现,但过去只有Authorization,Exception,Action,Result四种类型,现在又增加了一种Resource类型。所以共计五种。

    Resource类型Filter在Authorization类型Filter之后执行,但又在其它类型的Filter之前。且执行顺序也在Model Binding之前,所以可以对Model Binding产生影响。

    ASP.NET Core MVC框架中可以看到有ConsumesAttribute及FormatFilter两种实现IResourceFilter接口的类。

    ConsumesAttribute会按请求中的Content-Type(内容类型)进行过滤,而FormatFilter能对路由或路径中设置了format值的请求作过滤。

    一旦不符合要求,就对ResourceExecutingContext的Result属性设置,这样可以达到短路效果,阻止进行下面的处理。

    ConsumesAttribute类的例子:

    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        ...
    
        // Only execute if the current filter is the one which is closest to the action.
        // Ignore all other filters. This is to ensure we have a overriding behavior.
        if (IsApplicable(context.ActionDescriptor))
        {
            var requestContentType = context.HttpContext.Request.ContentType;
    
            // Confirm the request's content type is more specific than a media type this action supports e.g. OK
            // if client sent "text/plain" data and this action supports "text/*".
            if (requestContentType != null && !IsSubsetOfAnyContentType(requestContentType))
            {
                context.Result = new UnsupportedMediaTypeResult();
            }
        }
    }
    

    Filter在ASP.NET Core MVC里除了保留原有的包含同步方法的接口,现在又增加了包含异步方法的接口。

    同步

    • IActionFilter
    • IAuthorizationFilter
    • IExceptionFilter
    • IResourceFilter
    • IResultFilter

    异步

    • IAsyncActionFilter
    • IAsyncAuthorizationFilter
    • IAsyncExceptionFilter
    • IAsyncResourceFilter
    • IAsyncResultFilter

    新的接口不像旧有的接口包含两个同步方法,它们只有一个异步方法。但可以实现同样的功能。

    public class SampleAsyncActionFilter : IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(
            ActionExecutingContext context,
            ActionExecutionDelegate next)
        {
            // 在方法处理前执行一些操作
            var resultContext = await next();
            // 在方法处理后再执行一些操作。
        }
    }
    

    Attribute形式的Filter,其构造方法里只能传入一些基本类型的值,例如字符串:

    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", "Steve Smith @ardalis")]
    public class SampleController : Controller
    

    如果想要在其构造方法里引入其它类型的依赖,现在可以使用ServiceFilterAttribute,TypeFilterAttribute或者IFilterFactory方式。

    ServiceFilterAttribute需要在DI容器中注册:

    public class GreetingServiceFilter : IActionFilter
    {
        private readonly IGreetingService greetingService;
    
        public GreetingServiceFilter(IGreetingService greetingService)
        {
            this.greetingService = greetingService;
        }
    
        public void OnActionExecuting(ActionExecutingContext context)
        {
            context.ActionArguments["param"] = 
                this.greetingService.Greet("James Bond");
        }
    
        public void OnActionExecuted(ActionExecutedContext context)
        { }
    }
    
    
    services.AddScoped<GreetingServiceFilter>();
    
    
    [ServiceFilter(typeof(GreetingServiceFilter))]
    public IActionResult GreetService(string param)
    

    TypeFilterAttribute则没有必要:

    public class GreetingTypeFilter : IActionFilter
    {
        private readonly IGreetingService greetingService;
    
        public GreetingTypeFilter(IGreetingService greetingService)
        {
            this.greetingService = greetingService;
        }
    
        public void OnActionExecuting(ActionExecutingContext context)
        {
            context.ActionArguments["param"] = this.greetingService.Greet("Dr. No");
        }
    
        public void OnActionExecuted(ActionExecutedContext context)
        { }
    }
    
    [TypeFilter(typeof(GreetingTypeFilter))]
    public IActionResult GreetType1(string param)
    

    IFilterFactory也是不需要的:

    public class GreetingFilterFactoryAttribute : Attribute, IFilterFactory
    {
        public bool IsReusable => false;
    
        public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
        {
            var logger = (IGreetingService)serviceProvider.GetService(typeof(IGreetingService));
            return new GreetingFilter(logger);
        }
    
        private class GreetingFilter : IActionFilter
        {
            private IGreetingService _greetingService;
            public GreetingFilter(IGreetingService greetingService)
            {
                _greetingService = greetingService;
            }
            public void OnActionExecuted(ActionExecutedContext context)
            {
            }
    
            public void OnActionExecuting(ActionExecutingContext context)
            {
                context.ActionArguments["param"] = _greetingService.Greet("Dr. No");
            }
        }
        }
    
    [GreetingFilterFactory]
    public IActionResult GreetType1(string param)
    

    Filter有三种范围:

    • Global
    • Controller
    • Action

    后两种可以通过Attribute的方式附加到特定Action方法或者Controller类之上。对于Global,则要在ConfigureServices方法内部添加。

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc(options =>
        {
            // by instance
            options.Filters.Add(new AddDeveloperResultFilter("Tahir Naushad"));
    
            // by type
            options.Filters.Add(typeof(GreetDeveloperResultFilter)); 
        });
    
    }
    

    顾名思义,Global将对所有Controller及Action产生影响。所以务必对其小心使用。

    这三种范围的执行顺序在设计程序的时候也需要多作考虑:

    1. Global范围的前置处理代码
    2. Controller范围的前置处理代码
    3. Action范围的前置处理代码
    4. Action范围的后置处理代码
    5. Controller范围的后置处理代码
    6. Global范围的后置处理代码

    典型的前置处理代码如常见的OnActionExecuting方法,而常见的后置处理代码,则是像OnActionExecuted方法这般的。

  • 相关阅读:
    伐木工和森林的故事(一)
    EclipsePDT PHP的开发环境配置
    奇怪的using
    [团队开发]SERVER2008下无法安装VS2008 SP1 和 TFS2008 SP1补丁
    写在七夕
    一点点的松懈,就可以毁掉自己!
    2008,到今天我不后悔
    细节决定成败,注意的事情需要做到,而不是听完了当耳边风
    正视差距,展望2008!
    ZendStudio5.5调式环境配置
  • 原文地址:https://www.cnblogs.com/kenwoo/p/9532317.html
Copyright © 2011-2022 走看看