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

前言

前面一篇中讲解了过滤器执行之前的创建,通过实现IFilterProvider注册到当前的HttpConfiguration里的服务容器中,当然默认的基础服务也是有的,并且根据这些提供程序所获得的的过滤器信息集合进行排序。本篇就会对过滤器在创建完之后所做的一系列操作进行讲解。

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

FilterGrouping过滤器分组类型

FilterGrouping类型是ApiController类型中的私有类型,它的作用就如同它的命名一样,用来对过滤器集合进行分组,在上一篇中我们看到,在经过调用HttpActionDescriptor类型的GetFilterPipeline()方法之后回去获取到排序过后的过滤器信息集合Collection<FilterInfo>。下面我们看一下FilterGrouping类型定义:

示例代码1-1

    private class FilterGrouping
{
// Fields
private List<IActionFilter> _actionFilters = new List<IActionFilter>();
private List<IAuthorizationFilter> _authorizationFilters = new List<IAuthorizationFilter>();
private List<IExceptionFilter> _exceptionFilters = new List<IExceptionFilter>(); // Methods
public FilterGrouping(IEnumerable<FilterInfo> filters)
{
foreach (FilterInfo info in filters)
{
IFilter instance = info.Instance;
Categorize<IActionFilter>(instance, this._actionFilters);
Categorize<IAuthorizationFilter>(instance, this._authorizationFilters);
Categorize<IExceptionFilter>(instance, this._exceptionFilters);
}
} private static void Categorize<T>(IFilter filter, List<T> list) where T : class
{
T item = filter as T;
if (item != null)
{
list.Add(item);
}
} // Properties
public IEnumerable<IActionFilter> ActionFilters
{
get
{
return this._actionFilters;
}
} public IEnumerable<IAuthorizationFilter> AuthorizationFilters
{
get
{
return this._authorizationFilters;
}
} public IEnumerable<IExceptionFilter> ExceptionFilters
{
get
{
return this._exceptionFilters;
}
}
}

在代码1-1中我们看到在FilterGrouping类型的构造函数中便会对过滤器信息集合进行分组了,当然了分组的时候是调用FilterGrouping类型中的放吧,在Categorize()方法中就是根据实例的类型来进行判断的,最后由FilterGrouping类型中的三个公共属性来表示分组过后的不同类型的过滤器集合。

过滤器执行过程

在上个篇幅中我们通过示例了解到过滤器管道的生成过程以及结果,我们就来看一下执行的过程,顺带再看下过滤器管道的结果是不是如上篇上所说的那样。

先看服务器端(Selfhost)的代码:

代码1-2

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

这里只有一个添加全局行为过滤器的这么一句代码,其余的部分就不解释了。

然后我们接着看控制器部分,如下示例代码:

代码1-3

namespace NameSpaceControllerThree
{
[CustomControllerAuthorizationFilter]
[CustomControllerActionFilter]
public class WriterAndReadController : ApiController
{
[CustomActionFilter]
[CustomControllerActionAuthorizationFilter]
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-4

/// <summary>
/// 全局的行为过滤器
/// </summary>
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();
}
} /// <summary>
/// 控制器级行为过滤器
/// </summary>
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();
}
} /// <summary>
/// 控制器方法级行为过滤器
/// </summary>
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();
}
} /// <summary>
/// 控制器级授权访问过滤器
/// </summary>
public class CustomControllerAuthorizationFilterAttribute : FilterAttribute, IAuthorizationFilter
{ public Task<System.Net.Http.HttpResponseMessage> ExecuteAuthorizationFilterAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>> continuation)
{
Console.WriteLine(this.GetType().Name);
return continuation();
}
} /// <summary>
/// 控制器方法级授权访问过滤器
/// </summary>
public class CustomControllerActionAuthorizationFilterAttribute : FilterAttribute, IAuthorizationFilter
{ public Task<System.Net.Http.HttpResponseMessage> ExecuteAuthorizationFilterAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>> continuation)
{
Console.WriteLine(this.GetType().Name);
return continuation();
}
}

在代码1-4中,我们可以看到代码1-3中所有使用到的过滤器类型和代码1-2中添加全局过滤器类型。

现在我们看一下最后的结果

图1

黑色框的结果为SelfHost服务器端过滤器执行过程的输出,在代码1-4中我们可以看到,这个得出的一个结论是授权过滤器不管是什么应用范围的都是优于行为过滤器的,而在同一种类型的过滤器中是根据应用范围来确定执行顺序的,这个跟下面的浏览器里的内容有点关系,浏览器里显示的就是所有过滤器在排序后的管道里的样子,可以看到管道里单纯的就是按照应用范围的级别来排序的,至于这个过滤器是什么类型在处理排序的时候则是一点都不关心的。

过滤器执行过程-代码分析

首先看一下如下示意图,可以代表了在控制器执行的过程中过滤器的执行过程。

图2

上面通过示例来说明了过滤器的执行过程,现在我们来看一下在框架的源码中是什么样的,因为在过滤器执行过程中还包含了其它方面的知识点,所以这个是迟早都要看的,下面我们就来看一下吧。

代码1-5

     return InvokeActionWithExceptionFilters(InvokeActionWithAuthorizationFilters(actionContext, cancellationToken, authorizationFilters, () => actionDescriptor.ActionBinding.ExecuteBindingAsync(actionContext, cancellationToken).Then<HttpResponseMessage>(delegate {
this._modelState = actionContext.ModelState;
return InvokeActionWithActionFilters(actionContext, cancellationToken, actionFilters, () => controllerServices.GetActionInvoker().InvokeActionAsync(actionContext, cancellationToken))();
}, new CancellationToken(), false))(), actionContext, cancellationToken, exceptionFilters);

在代码1-5中涉及到三个静态方法,我们先来看一下:

InvokeActionWithExceptionFilters()

InvokeActionWithAuthorizationFilters()

InvokeActionWithActionFilters()

示例代码如下:

代码1-6

    internal static Task<HttpResponseMessage> InvokeActionWithExceptionFilters(Task<HttpResponseMessage> actionTask, HttpActionContext actionContext, CancellationToken cancellationToken, IEnumerable<IExceptionFilter> filters)
{
return actionTask.Catch<HttpResponseMessage>(delegate (CatchInfo<HttpResponseMessage> info) {
HttpActionExecutedContext executedContext = new HttpActionExecutedContext(actionContext, info.Exception);
filters = filters.Reverse<IExceptionFilter>();
IEnumerable<Task> asyncIterator = from filter in filters select filter.ExecuteExceptionFilterAsync(executedContext, cancellationToken);
bool runSynchronously = true;
Task<HttpResponseMessage> task = TaskHelpers.Iterate(asyncIterator, cancellationToken, true).Then<HttpResponseMessage>(delegate {
if (executedContext.Response != null)
{
return TaskHelpers.FromResult<HttpResponseMessage>(executedContext.Response);
}
return TaskHelpers.FromError<HttpResponseMessage>(executedContext.Exception);
}, new CancellationToken(), runSynchronously);
return info.Task(task);
}, new CancellationToken());
}

代码1-7

    internal static Func<Task<HttpResponseMessage>> InvokeActionWithAuthorizationFilters(HttpActionContext actionContext, CancellationToken cancellationToken, IEnumerable<IAuthorizationFilter> filters, Func<Task<HttpResponseMessage>> innerAction)
{
filters = filters.Reverse<IAuthorizationFilter>();
return filters.Aggregate<IAuthorizationFilter, Func<Task<HttpResponseMessage>>>(innerAction, (continuation, filter) => () => filter.ExecuteAuthorizationFilterAsync(actionContext, cancellationToken, continuation));
}

代码1-8

    internal static Func<Task<HttpResponseMessage>> InvokeActionWithActionFilters(HttpActionContext actionContext, CancellationToken cancellationToken, IEnumerable<IActionFilter> filters, Func<Task<HttpResponseMessage>> innerAction)
{
filters = filters.Reverse<IActionFilter>();
return filters.Aggregate<IActionFilter, Func<Task<HttpResponseMessage>>>(innerAction, (continuation, filter) => () => filter.ExecuteActionFilterAsync(actionContext, cancellationToken, continuation));
}

这里我们先看代码1-5表示了过滤器执行的所有过程,突然的看起来这1-5代码的可读性太低了,可能是跟我水平的关系,我看起来很是吃力也比较烦躁,不过这个烂骨头也要啃阿,放过去可能就少学会一点东西。

首先我们看到代码1-5中调用了InvokeActionWithExceptionFilters()方法,也就是代码1-6,那我们就看看这个InvokeActionWithExceptionFilters()方法,在个InvokeActionWithExceptionFilters()方法中有四个参数,第一个参数是Task<HttpResponseMessage>类型的,这里打住不往下看了,回到代码1-5中调用个InvokeActionWithExceptionFilters()方法的时候,我们看代码的最后部分依次往前推,最后发现

代码1-9

InvokeActionWithAuthorizationFilters(actionContext, cancellationToken, authorizationFilters, () => actionDescriptor.ActionBinding.ExecuteBindingAsync(actionContext, cancellationToken).Then<HttpResponseMessage>(delegate {
this._modelState = actionContext.ModelState;
return InvokeActionWithActionFilters(actionContext, cancellationToken, actionFilters, () => controllerServices.GetActionInvoker().InvokeActionAsync(actionContext, cancellationToken))();
}, new CancellationToken(), false))(),

发现代码1-9的部分就是InvokeActionWithExceptionFilters()方法的参数,我们看命名也都知道InvokeActionWithExceptionFilters()方法执行的是异常过滤器的内容,第一个参数类型也说过了是Task<HttpResponseMessage>说明在这之前操作已经处理完成了不管是成功了还是有异常了咱先不管,反正代码1-9最后生成返回的就是Task<HttpResponseMessage>类型的实例,那我们就来拆开代码1-9.

从代码1-9中可以看到首先调用的是代码1-7的内容也就是调用了InvokeActionWithAuthorizationFilters()方法,我们看一下代码1-7.

首先代码1-7中的方法有四个参数(HttpActionContext actionContext, CancellationToken cancellationToken, IEnumerable<IAuthorizationFilter> filters, Func<Task<HttpResponseMessage>> innerAction),第一个控制器方法执行上下文对象,跟HttpControllerContext性质都是一样的,不说这个,第二个CancellationToken用于并行开发在并行的任务中,可以把这个类型想象成一个钩子,你可以设置这个钩子的状态和行为,在任务中遇到你的钩子会根据你的钩子做一些操作可以是继续任务可以是终止任务额外再执行一些其他操作(不知道理解的对不对没深入过,有误的话望大家指正谢谢),至于第三个参数,就是授权过滤器集合类型了,在上面说到的FilterGrouping类型中的AuthorizationFilters属性就是用在这里,第四个参数就比较重要了,这是一个返回Task<HttpResponseMessage>类型的委托,现在我们把代码1-9也就是调用了InvokeActionWithAuthorizationFilters()方法的代码中的第四个参数剥出来,然后再看下面的代码。

代码1-10

InvokeActionWithAuthorizationFilters(actionContext, cancellationToken, authorizationFilters, )()

从上面说的也知道现在的代码1-10的部分只是返回一个Task<HttpResponseMessage>类型的实例作为代码1-6的第一个参数,按照这样的思路我们看一下剥离出来的第四个参数。

代码1-11

() => actionDescriptor.ActionBinding.ExecuteBindingAsync(actionContext, cancellationToken).Then<HttpResponseMessage>(delegate {
this._modelState = actionContext.ModelState;
return InvokeActionWithActionFilters(actionContext, cancellationToken, actionFilters, () => controllerServices.GetActionInvoker().InvokeActionAsync(actionContext, cancellationToken))();
}, new CancellationToken(), false)

在代码1-11里主要会先调用actionDescriptor的ActionBinding属性下的ExecuteBindingAsync()方法,这里的方法就是Model绑定Model验证所在的地方了这个后面的篇幅会讲,有的朋友会发现ExecuteBindingAsync()方法返回的是Task类型,跟上面所说的所需参数的类型是Fun<Task<HttpResponseMessage>>,而这里明显就是Fun<Task>,是不符合的,而且按照逻辑上说也不符合阿,在授权过滤器执行完毕后应该是行为过滤器的执行阿,这里就涉及到了一个Task的扩展方法调用,就是Then<>()方法了。

代码1-12

internal static Task<TOuterResult> Then<TOuterResult>(this Task task, Func<Task<TOuterResult>> continuation, CancellationToken cancellationToken = new CancellationToken(), bool runSynchronously = false)
{
return task.ThenImpl<Task, TOuterResult>(t => continuation(), cancellationToken, runSynchronously);
}

用有扩展方法的类型是私有的结构类型,这里就不往下深入了,就在通过这里将Task转换成Task<HttpResponseMessage>类型的。最后我们在拆一下把这个匿名委托从代码1-11里面剥出来。

代码1-13

delegate {
this._modelState = actionContext.ModelState;
return InvokeActionWithActionFilters(actionContext, cancellationToken, actionFilters, () => controllerServices.GetActionInvoker().InvokeActionAsync(actionContext, cancellationToken))();
}

看到这里有actionContext.ModelState属性值表示Model验证的结果值,而这个this._modelState的this就是当前的ApiController,_modelState字段对应的是ApiController类型中的ModelState值,在这之后调用最后的1-8代码,在上面的1-13中我们可以看到最后是由什么对象去执行最后的操作的,这个一系列的过程后面篇幅会讲解到。

在这些所有都执行完毕了之后才会执行到代码1-6,最后就是形成最后的代码1-5那样。

作者:金源

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

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

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

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

    ASP.NET Web API 过滤器创建.执行过程(一) 前言 在上一篇中我们讲到控制器的执行过程系列,这个系列要搁置一段时间了,因为在控制器执行的过程中包含的信息都是要单独的用一个系列来描述的,就 ...

  2. ASP.NET Web API 控制器创建过程(二)

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

  3. ASP.NET Web API 控制器创建过程(一)

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

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

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

  5. [转]使用ASP.NET Web API 2创建OData v4 终结点

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

  6. ASP.NET Web API 2 过滤器

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

  7. 使用ASP.NET web API创建REST服务(二)

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

  8. 使用ASP.NET web API创建REST服务(三)

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

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

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

随机推荐

  1. 操作系统篇-调用门与特权级(CPL、DPL和RPL)

    || 版权声明:本文为博主原创文章,未经博主允许不得转载. 一.前言 在前两篇文章(<操作系统篇-浅谈实模式与保护模式>和<操作系统篇-分段机制与GDT|LDT>)中,我们提到 ...

  2. 终于等到你:CYQ.Data V5系列 (ORM数据层)最新版本开源了

    前言: 不要问我框架为什么从收费授权转到免费开源,人生没有那么多为什么,这些年我开源的东西并不少,虽然这个是最核心的,看淡了就也没什么了. 群里的网友:太平说: 记得一年前你开源另一个项目的时候我就说 ...

  3. 解决 Springboot Unable to build Hibernate SessionFactory @Column命名不起作用

    问题: Springboot启动报错: Caused by: org.springframework.beans.factory.BeanCreationException: Error creati ...

  4. Dapper where Id in的解决方案

    简单记一下,一会出去有点事情~ 我们一般写sql都是==>update NoteInfo set NDataStatus=@NDataStatus where NId in (@NIds) Da ...

  5. 自己实现一个javascript事件模块

    nodejs中的事件模块 nodejs中有一个events模块,用来给别的函数对象提供绑定事件.触发事件的能力.这个别的函数的对象,我把它叫做事件宿主对象(非权威叫法),其原理是把宿主函数的原型链指向 ...

  6. 如何避免git每次提交都输入密码

    在ubuntu系统中,如何避免git每次提交都输入用户名和密码?操作步聚如下:1: cd 回车: 进入当前用户目录下:2: vim .git-credentials (如果没有安装vim 用其它编辑器 ...

  7. springMVC初探--环境搭建和第一个HelloWorld简单项目

    注:此篇为学习springMVC时,做的笔记整理. MVC框架要做哪些事情? a,将url映射到java类,或者java类的方法上 b,封装用户提交的数据 c,处理请求->调用相关的业务处理—& ...

  8. JDBC MySQL 多表关联查询查询

    public static void main(String[] args) throws Exception{ Class.forName("com.mysql.jdbc.Driver&q ...

  9. 【夯实Nginx基础】Nginx工作原理和优化、漏洞

    本文地址 原文地址 本文提纲: 1.  Nginx的模块与工作原理    2.  Nginx的进程模型    3 . NginxFastCGI运行原理        3.1 什么是 FastCGI   ...

  10. Block解析(iOS)

    1. 操作系统中的栈和堆 我们先来看看一个由C/C++/OBJC编译的程序占用内存分布的结构: 栈区(stack):由系统自动分配,一般存放函数参数值.局部变量的值等.由编译器自动创建与释放.其操作方 ...