ASP.NET Web API 过滤器创建、执行过程(一)

时间:2022-06-29 19:44:35

ASP.NET Web API 过滤器创建、执行过程(一)

前言

在上一篇中我们讲到控制器的执行过程系列,这个系列要搁置一段时间了,因为在控制器执行的过程中包含的信息都是要单独的用一个系列来描述的,就如今天的这个篇幅就是在上面内容之后所看到的一个知识要点之一。

ASP.NET Web API 过滤器创建、执行过程(一)

下面就来讲解一下在ASP.NET Web API框架中过滤器的创建、执行过程。

过滤器所在的位置

图1

ASP.NET Web API 过滤器创建、执行过程(一)

图1所示的就是控制器执行过程很粗略的表示。

通过上一篇内容我们了解到控制器方法选择器最后返回的并不是控制器方法,而是对于控制器方法描述的类型HttpActionDescriptor,HttpActionDescriptor包含了控制器方法的一切信息,今天要讲的就是HttpActionDescriptor对象中生成的过滤器管道执行的这么一个顺序,当然其中就已经包含了创建的时候。

在介绍HttpActionDescriptor类型生成过滤器管道之前,我们先来对着其中会涉及到的一些类型进行一个基础的了解。

基础类型一览

FilterInfo 过滤器对象封装信息(System.Web.Http.Filters

示例代码1-1

    public sealed class FilterInfo
{
public FilterInfo(IFilter instance, FilterScope scope); public IFilter Instance { get; }
public FilterScope Scope { get; }
}

在代码1-1中想必大家也看到了,FilterInfo类型中有两属性,一个是有着过滤器类型的实例对象的引用也就是IFilter类型的Instance属性,还有一个是FilterScope类型的Scope属性表示当前这个过滤器在项目中的应用范围,这个值很重要,在过滤器管道中可是根据这个值来排序的。

FilterScope 过滤器应用范围(System.Web.Http.Filters

示例代码1-2

    public enum FilterScope
{
// 摘要:
// 在 Controller 之前指定一个操作。
Global = ,
//
// 摘要:
// 在 Action 之前和 Global 之后指定一个顺序。
Controller = ,
//
// 摘要:
// 在 Controller 之后指定一个顺序。
Action = ,
}

从代码1-2中一目了然了,这里就不多说了。

IFilter 过滤器顶层接口(System.Web.Http.Filters

示例代码1-3

    public interface IFilter
{
// 摘要:
// 获取或设置一个值,该值指示是否可以为单个程序元素指定多个已指示特性的实例。
//
// 返回结果:
// 如果可以指定多个实例,则为 true;否则为 false。默认值为 false。
bool AllowMultiple { get; }
}

FilterAttribute 过滤器默认实现特性类(System.Web.Http.Filters

    // 摘要:
// 表示操作-筛选器特性的基类。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public abstract class FilterAttribute : Attribute, IFilter
{
// 摘要:
// 初始化 System.Web.Http.Filters.FilterAttribute 类的新实例。
protected FilterAttribute(); // 摘要:
// 获取用于指示是否允许多个筛选器的值。
//
// 返回结果:
// 如果允许多个筛选器,则为 true;否则为 false。
public virtual bool AllowMultiple { get; }
}

示例代码1-4中我们可以看到FilterAttribute类型为过滤器默认的特性类型,而我们如果要想实现自定义的过滤器仅仅靠继承自FilterAttribute是不行的,因为FilterAttribute特性类只是属于过滤器概念中的“属性”,而过滤器中的行为则是由过滤器类型来控制器的,也就是下面要说的。

IActionFilter 行为过滤器接口(System.Web.Http.Filters

示例代码1-5

    public interface IActionFilter : IFilter
{
// 摘要:
// 异步执行筛选器操作。
//
// 参数:
// actionContext:
// 操作上下文。
//
// cancellationToken:
// 为此任务分配的取消标记。
//
// continuation:
// 在调用操作方法之后,委托函数将继续。
//
// 返回结果:
// 为此操作新建的任务。
Task<System.Net.Http.HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>> continuation);
}

这里暂时不对行为过滤器接口类型做什么讲解,在最后的示例中会有运用到,而对于剩下的验证过滤器和异常过滤器接口也都差不多了,这里就单独的演示一个行为过滤器。

过滤器管道生成过程

这里我们还是要说到之前说过的HttpActionDescriptor类型,我来说一下大概的过程,首先呢在HttpActionDescriptor类型中有个内部字段叫_filterPipeline,从命名上就可以看出来大概的意思了,对的,过滤器管道的信息就是在这个字段中的,而它是个Lazy<Collection<FilterInfo>>类型。

在ApiController的执行中有个私有类型是FilterGrouping类型,它这个类型的作用是对过滤器管道中的所有过滤器进行分类,就是验证归验证的,行为是行为的。

而实例化FilterGrouping类型的时候构造函数需要Collection<FilterInfo>类型的参数(也就是过滤器管道)来进行实例化,这个时候就是HttpActionDescriptor类型一展身手的时候了,看代码1-6就是HttpActionDescriptor类型重的函数。

示例代码1-6

    public virtual Collection<FilterInfo> GetFilterPipeline()
{
return this._filterPipeline.Value;
}

在上面我们也说到了_filterPipeline这个字段,那么它的Value在这个时候做了什么呢?

代码1-6.1

this._filterPipeline =  new Lazy<Collection<FilterInfo>>(new Func<Collection<FilterInfo>>(this.InitializeFilterPipeline));

看到这里我们只需要关注InitializeFilterPipeline这个函数的实现。

代码1-6.2

    private Collection<FilterInfo> InitializeFilterPipeline()
{
return new Collection<FilterInfo>(RemoveDuplicates((from fp in this._configuration.Services.GetFilterProviders() select fp.GetFilters(this._configuration, this)).OrderBy<FilterInfo, FilterInfo>(f => f, FilterInfoComparer.Instance).Reverse<FilterInfo>()).Reverse<FilterInfo>().ToList<FilterInfo>());
}

首先我们从这里看到的是,会从HttpConfiguration中的服务容器中获取基础服务,也就是实现了IFilterProvider的服务,而在其中也是有两个过滤器提供程序,下面我直接贴上源码

示例代码1-7

    public class ConfigurationFilterProvider : IFilterProvider
{
// Methods
public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
return configuration.Filters;
}
}

在代码1-7中返回的就是HttpConfiguration中的Filters属性中的值。这里了解一下继续往下看,

代码1-8

    public class ActionDescriptorFilterProvider : IFilterProvider
{
// Methods
public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
if (actionDescriptor == null)
{
throw Error.ArgumentNull("actionDescriptor");
}
IEnumerable<FilterInfo> first = from instance in actionDescriptor.ControllerDescriptor.GetFilters() select new FilterInfo(instance, FilterScope.Controller);
IEnumerable<FilterInfo> second = from instance in actionDescriptor.GetFilters() select new FilterInfo(instance, FilterScope.Action);
return first.Concat<FilterInfo>(second);
}
}

在代码1-7中返回的是注册在全局范围使用的过滤器,而在代码1-8中则是控制器和控制器方法范围的过滤器。

这个时候我们再看代码1-6.2中的FilterInfoComparer.Instance的默认实现。

代码1-9

    public int Compare(FilterInfo x, FilterInfo y)
{
if ((x == null) && (y == null))
{
return ;
}
if (x == null)
{
return -;
}
if (y == null)
{
return ;
}
return (int) (x.Scope - y.Scope);
}

过滤器管道生成结果示例

看到这里大家想必已经知道了返回的过滤器管道中是什么样子的了吧,如果不清楚也没关系,下面的示例来说明一下。

同种类型过滤器覆盖面的执行优先级:

服务端(SelfHost):

代码1-10

using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using NameSpaceControllerThree; namespace SelfHost
{
class Program
{
static void Main(string[] args)
{ HttpSelfHostConfiguration selfHostConfiguration =
new HttpSelfHostConfiguration("http://localhost/selfhost");
using (HttpSelfHostServer selfHostServer = new HttpSelfHostServer(selfHostConfiguration))
{
selfHostServer.Configuration.Routes.MapHttpRoute(
"DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional });
selfHostServer.Configuration.Services.Replace(typeof(IAssembliesResolver),
new CustomAssembliesResolver.LoadSpecifiedAssembliesResolver());
//添加全局过滤器
selfHostServer.Configuration.Filters.Add(new WebAPIController.Filter.CustomConfigurationActionFilterAttribute());
selfHostServer.OpenAsync();
Console.WriteLine("服务器端服务监听已开启");
Console.Read();
}
}
}
}

在服务端我们在HttpConfiguration中的Fileters属性中添加上了WebAPIController.Filter.CustomConfigurationActionFilterAttribute这个类型,下面会有说到。

在WebAPIController项目中我会定义有控制器,以及同种过滤器的三种应用范围(用类型名称来区别了)。

首先我们看一下控制器类型的定义:

代码1-11

namespace NameSpaceControllerThree
{
[CustomControllerActionFilter]
public class WriterAndReadController : ApiController
{
[CustomActionFilter]
public string Get()
{
StringBuilder strBuilder = new StringBuilder();
HttpActionDescriptor actionDescriptor = this.Configuration.Services.GetActionSelector().SelectAction(this.ControllerContext);
System.Collections.ObjectModel.Collection<FilterInfo> filtersInfo = actionDescriptor.GetFilterPipeline();
foreach (var filter in filtersInfo)
{
strBuilder.AppendLine("【FilterName:"+filter.Instance.GetType().Name+",FilterScope:"+filter.Scope.ToString()+"】");
}
return strBuilder.ToString();
}
}
}

可能看到这里对于下面自定义的行为过滤器会很感兴趣,那么就一起来看一下吧。

代码1-12

    public class CustomConfigurationActionFilterAttribute : FilterAttribute, IActionFilter
{ public Task<System.Net.Http.HttpResponseMessage> ExecuteActionFilterAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>> continuation)
{
//Console.WriteLine(this.GetType().Name);
return continuation();
}
} public class CustomControllerActionFilterAttribute : FilterAttribute, IActionFilter
{ public Task<System.Net.Http.HttpResponseMessage> ExecuteActionFilterAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>> continuation)
{
//Console.WriteLine(this.GetType().Name);
return continuation();
}
} public class CustomActionFilterAttribute : FilterAttribute, IActionFilter
{ public Task<System.Net.Http.HttpResponseMessage> ExecuteActionFilterAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>> continuation)
{
//Console.WriteLine(this.GetType().Name);
return continuation();
}
}

我这里是定义的三个行为过滤器,在默认实现中为了方便直接是调用continuation委托来进行操作,便于外部的叠加器使用。

这个时候我们在运行起来服务端过后,不管是通过浏览器访问还是客户端访问都可以看到如下的结果图。

图2

ASP.NET Web API 过滤器创建、执行过程(一)

作者:金源

出处:http://www.cnblogs.com/jin-yuan/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面

ASP.NET Web API 过滤器创建、执行过程(一)的更多相关文章

  1. ASP&period;NET Web API 过滤器创建、执行过程&lpar;二&rpar;

    ASP.NET Web API 过滤器创建.执行过程(二) 前言 前面一篇中讲解了过滤器执行之前的创建,通过实现IFilterProvider注册到当前的HttpConfiguration里的服务容器 ...

  2. ASP&period;NET Web API 控制器创建过程&lpar;二&rpar;

    ASP.NET Web API 控制器创建过程(二) 前言 本来这篇随笔应该是在上周就该写出来发布的,由于身体跟不上节奏感冒发烧有心无力,这种天气感冒发烧生不如死,也真正的体会到了什么叫病来如山倒,病 ...

  3. ASP&period;NET Web API 控制器创建过程&lpar;一&rpar;

    ASP.NET Web API 控制器创建过程(一) 前言 在前面对管道.路由有了基础的了解过后,本篇将带大家一起学习一下在ASP.NET Web API中控制器的创建过程,这过程分为几个部分下面的内 ...

  4. 使用ASP&period;NET Web API 2创建OData v4 终结点

    开放数据协议(Open Data Protocol[简称OData])是用于Web的数据访问协议.OData提供了一种对数据集进行CRUD操作(Create,Read,Update,Delete)的统 ...

  5. &lbrack;转&rsqb;使用ASP&period;NET Web API 2创建OData v4 终结点

    本文转自:http://www.cnblogs.com/farb/p/ODataAspNetWebAPI.html 开放数据协议(Open Data Protocol[简称OData])是用于Web的 ...

  6. ASP&period;NET Web API 2 过滤器

    Ø  前言 我们知道 ASP.NET Web API 过滤器,也是属于消息处理机制中的一部分.正因如此,我们经常使用它来完成对请求的授权验证.参数验证,以及请求的 Log 记录,程序异常捕获等. 1. ...

  7. 使用ASP&period;NET web API创建REST服务&lpar;二&rpar;

    Creating a REST service using ASP.NET Web API A service that is created based upon the architecture ...

  8. 使用ASP&period;NET web API创建REST服务&lpar;三&rpar;

    本文档来源于:http://www.cnblogs.com/madyina/p/3390773.html Creating a REST service using ASP.NET Web API A ...

  9. 剖析Asp&period;Net Web API中HttpController的激活

    在Asp.Net Web API中,请求的目标是定义在某个HttpController中的某个Action方法.当请求经过Asp.Net Web API消息处理管道到达管道"龙尾" ...

随机推荐

  1. js 实现类似php函数number&lowbar;format的功能

    今天同事在做一个功能的时候需要使用js来实现类似php函数number_format的功能,最后就有了下面的方法,可以实现了: /** * number_format * @param number ...

  2. AS3全局与局部坐标转换

    在大部分需要用户点击的游戏中,坐标的转换是一种必须熟练掌握的方法. 首先在一个700x700的舞台中创建2个方块,红色的大方块A是600x600,位于(50,50),绿色的小方块B是300x300.A ...

  3. JVM 垃圾回收算法

    在说垃圾回收算法之前,先谈谈JVM怎样确定哪些对象是“垃圾”. 1.引用计数器算法: 引用计数器算法是给每个对象设置一个计数器,当有地方引用这个对象的时候,计数器+1,当引用失效的时候,计数器-1,当 ...

  4. &lbrack;C&num;详解&rsqb; &lpar;1&rpar; 自动属性、初始化器、扩展方法

    文章来源:Slark.NET-博客园 http://www.cnblogs.com/slark/p/CSharp-focus-1.html 代码下载:点我下载 目录 前言 属性与自动属性 属性 自动属 ...

  5. cf&period;295&period;C&period;DNA Alignment(数学推导)

    DNA Alignment time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...

  6. mysql explain 命令讲解

    explian命令可以显示select语句的执行计划 explain的结果中每行对应select语句中的一个表,输出结果中的顺序是按照语句处理表的顺序. mysql使用嵌套循环来处理所有的join连接 ...

  7. 集合视图控制器&lpar;CollectionViewController&rpar; 、 标签控制器&lpar;TabBarController&rpar; 、 高级控件介绍

    1 创建集合视图,设置相关属性以满足要求 1.1 问题 集合视图控制器UIConllectionViewController是一个展示大量数据的控制器,系统默认管理着一个集合视图UICollectio ...

  8. UITextField swift

    // // ViewController.swift // UILabelTest // // Created by mac on 15/6/23. // Copyright (c) 2015年 fa ...

  9. POJ3126 Prime Path

    http://poj.org/problem?id=3126 题目大意:给两个数四位数m, n, m的位数各个位改变一位0 —— 9使得改变后的数为素数, 问经过多少次变化使其等于n 如: 10331 ...

  10. JS HTML标签尺寸距离位置定位计算

    四种浏览器对 clientHeight.offsetHeight.scrollHeight.clientWidth.offsetWidth 和 scrollWidth 的解释差异 网页可见区域宽:do ...