ASP.NET Web API的消息处理管道可以理解为请求到达Controller之前、Controller返回响应之后的处理机制。之所以需要了解消息处理管道,是因为我们可以借助它来实现对请求和响应的自定义处理。所有的请求被封装到HttpRequestMessage这个类中,所有的响应被封装到HttpResponseMessage这个类中。

既然消息处理管道是可扩展的,那么,ASP.NET Web API一定为我们准备了便于扩展的接口或抽象类,它就是HttpMessageHandler抽象类。

  1. namespace System.Net.Http
  2.  
  3. {
  4.  
  5.     public abstract class HttpMessageHandler : IDisposable
  6.  
  7.     {
  8.  
  9.         protected HttpMessageHandler()
  10.  
  11.         {
  12.  
  13.         }
  14.  
  15.         protected internal abstract Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
  16.  
  17.         protected virtual void Dispose(bool disposing)
  18.  
  19.         {
  20.  
  21.         }
  22.  
  23.         public void Dispose()
  24.  
  25.         {
  26.  
  27.             this.Dispose(true);
  28.  
  29.             GC.SuppressFinalize((object)this);
  30.  
  31.         }
  32.  
  33.     }
  34.  
  35. }
  36.  

这个抽象基类,把处理请求响应交给了SendAsync方法,而且是以异步的方式处理的。既然这里没有提供SendAsync方法的具体实现,所以HttpMessageHandler抽象类一定有一个派生类,它就是DelegatingHandler类。

  1. public abstract class DelegatingHandler : HttpMessageHandler
  2.  
  3. {
  4.  
  5.     private HttpMessageHandler innerHandler;
  6.  
  7.     protected DelegatingHandler(HttpMessageHandler innerHandler)
  8.  
  9.     {
  10.  
  11.         this.innerHandler = innerHandler;
  12.  
  13.     }
  14.  
  15.     public HttpMessageHandler InnerHandler
  16.  
  17.     {
  18.  
  19.         get
  20.  
  21.         {
  22.  
  23.             return this.innerHandler;
  24.  
  25.         }
  26.  
  27.         set
  28.  
  29.         {
  30.  
  31.             ...
  32.  
  33.             this.innerHandler = value;
  34.  
  35.         }
  36.  
  37.     }
  38.  
  39.     protected internal override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  40.  
  41.     {
  42.  
  43.         if(request == null)
  44.  
  45.         {
  46.  
  47.             throw new ArgumentNullException("request");
  48.  
  49.         }
  50.  
  51.         ...
  52.  
  53.         return this.innerHandler.SendAsync(request, cancellationToken);
  54.  
  55.     }
  56.  
  57. }
  58.  

比较有意思的是,DelegatingHandler本身是一个HttpMessageHandler类型,却还在它的构造函数中注入一个HttpMessageHandler类型,并且在SendAsync方法中,让注入的HttpMessageHandler类型执行SendAsync方法,这形成了一个HttpMessageHandler类型的链条。从这点来说,消息处理管道并不是只有一个人在战斗,而是,只要派生于DelegatingHandler这个类,不管是内置的,还是自定义的,都可以对请求响应作处理。

而在消息处理管道中肯定有一个打头阵的人,这个人就是HttpServer类。

  1. public class HttpServer : DelegatingHandler
  2.  
  3. {
  4.  
  5.     public HttpConfiguration Configuration{get;}
  6.  
  7.     public HttpMessageHandler Dispatcher{get;}
  8.  
  9.     public HttpServer();
  10.  
  11.     public HttpServer(HttpMessageHandler dipatcher);
  12.  
  13.     public HttpServer(HttpConfiguration configuration);
  14.  
  15.     public HttpServer(HttpConfiguration configuration, HttpMessageHandler dispatcher);
  16.  
  17.     protected overrde void Dispose(bool disposing); //处理HttpConfiguration对象,因为该对象实现了IDisposable接口
  18.  
  19.     protected virutal void Initialize();
  20.  
  21.     protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cacellationToken);
  22.  
  23.     //别忘了,HttpServer的父类DelegatingHandler还有一个InnerHandler属性。
  24.  
  25. }
  26.  

这里的HttpServer当然是可以实例化的,每一次实例化意味着创建了HttpMessageHandler类型的链条的头,就是HttpServer本身,也创建了链条的尾,就是Dispatcher属性所表示的HttpMessageHandler类型。

HttpConfiguration又是什么呢?

  1. public class HttpConfiguration : IDisposable
  2.  
  3. {
  4.  
  5.     ...
  6.  
  7.     public Collection<DelegatingHandler> MessageHandlers{get;}
  8.  
  9. }

原来,我们可以从HttpConfiguration的MessageHandlers属性中获取所有的HttpMessageHandler类型,当然也可以把自定义的HttpMessageHandler类型注册到这个MessageHandlers集合中去。

到这,已经蠢蠢欲动,跃跃欲试了,自定义DelegatingHandler可以登场了!大致是:

HttpServer→自定义DelegatingHandler→HttpControllerDispatcher

举例:重新设置HttpRequestMessage.Method属性

一般情况下,客户端,比如浏览器可以发出GET、POST、HEAD、PUT、DELETE请求,当请求进入消息处理管道,我们可以通过HttpRequestMessage.Method属性获取到这些动作类型。但有些客户端却只能发出GET、POST请求中,诸如HEAD、PUT、DELETE等请求必须放到请求报文的X-HTTP-Method-Override属性中,在这种情况下,我们需要把X-HTTP-Method-Override属性值赋值给HttpRequestMessage.Method属性。

创建一个ASP.NET MVC4项目,选择ASP.NET Web API模版,就如"ASP.NET Web API实践系列02,在MVC4下的一个实例, 包含EF Code First,依赖注入, Bootstrap等"中一样。

自定义一个派生于DelegatingHandler的类。

  1. using System;
  2.  
  3. using System.Linq;
  4.  
  5. using System.Net.Http;
  6.  
  7. using System.Threading;
  8.  
  9. using System.Threading.Tasks;
  10.  
  11. namespace ControlAndRoute.Extension
  12.  
  13. {
  14.  
  15.     public class MethodOverrideHandler : DelegatingHandler
  16.  
  17.     {
  18.  
  19.         private readonly string[] _methods = {"DELETE", "HEAD", "PUT"};
  20.  
  21.         private const string _header = "X-HTTP-Method-Override";
  22.  
  23.         protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  24.  
  25.         {
  26.  
  27.             if (request.Method == HttpMethod.Post && request.Headers.Contains(_header))
  28.  
  29.             {
  30.  
  31.                 //从请求头中获取X-HTTP-Method-Override的属性值
  32.  
  33.                 var method = request.Headers.GetValues(_header).FirstOrDefault();
  34.  
  35.                 if (_methods.Contains(method, StringComparer.InvariantCultureIgnoreCase))
  36.  
  37.                 {
  38.  
  39.                     request.Method = new HttpMethod(method);
  40.  
  41.                 }
  42.  
  43.             }
  44.  
  45.             return base.SendAsync(request, cancellationToken);
  46.  
  47.         }
  48.  
  49.     }
  50.  
  51. }
  52.  

在App_Start文件夹下的WebApiConfig类中注册MethodOverrideHandler类。

  1. using System.Web.Http;
  2.  
  3. using ControlAndRoute.Extension;
  4.  
  5. namespace ControlAndRoute
  6.  
  7. {
  8.  
  9.     public static class WebApiConfig
  10.  
  11.     {
  12.  
  13.         public static void Register(HttpConfiguration config)
  14.  
  15.         {
  16.  
  17.             //注册自定义的DelegatingHandler
  18.  
  19.             config.MessageHandlers.Add(new MethodOverrideHandler());
  20.  
  21.             config.Routes.MapHttpRoute(
  22.  
  23.                 name: "DefaultApi",
  24.  
  25.                 routeTemplate: "api/{controller}/{id}",
  26.  
  27.                 defaults: new { id = RouteParameter.Optional }
  28.  
  29.             );
  30.  
  31.             // 取消注释下面的代码行可对具有 IQueryable 或 IQueryable<T> 返回类型的操作启用查询支持。
  32.  
  33.             // 若要避免处理意外查询或恶意查询,请使用 QueryableAttribute 上的验证设置来验证传入查询。
  34.  
  35.             // 有关详细信息,请访问 http://go.microsoft.com/fwlink/?LinkId=279712。
  36.  
  37.             //config.EnableQuerySupport();
  38.  
  39.             // 若要在应用程序中禁用跟踪,请注释掉或删除以下代码行
  40.  
  41.             // 有关详细信息,请参阅: http://www.asp.net/web-api
  42.  
  43.             config.EnableSystemDiagnosticsTracing();
  44.  
  45.         }
  46.  
  47.     }
  48.  
  49. }
  50.  

在ValuesController中public void Put(int id, [FromBody]string value)的方法体内打上断点。打开Fiddler,输入如下:

发出的Put请求,被消息处理管道接收、处理,程序停在Put方法内的断点处。

ASP.NET Web API实践系列05,消息处理管道的更多相关文章

  1. ASP.NET Web API实践系列04,通过Route等特性设置路由

    ASP.NET Web API路由,简单来说,就是把客户端请求映射到对应的Action上的过程.在"ASP.NET Web API实践系列03,路由模版, 路由惯例, 路由设置"一 ...

  2. ASP.NET Web API实践系列07,获取数据, 使用Ninject实现依赖倒置,使用Knockout实现页面元素和视图模型的双向绑定

    本篇接着上一篇"ASP.NET Web API实践系列06, 在ASP.NET MVC 4 基础上增加使用ASP.NET WEB API",尝试获取数据. 在Models文件夹下创 ...

  3. ASP.NET Web API实践系列09,在Fiddler和控制台中模拟GET和POST请求

    ASP.NET Web API本质是由一个进程托管的一组类,需要宿主,这个宿主可以是ASP.NET应用程序,可以是MVC项目,可以是控制台应用程序,也可以是自己定制的宿主. 在VS2012中创建一个& ...

  4. ASP.NET Web API实践系列06, 在ASP.NET MVC 4 基础上增加使用ASP.NET WEB API

    本篇尝试在现有的ASP.NET MVC 4 项目上增加使用ASP.NET Web API. 新建项目,选择"ASP.NET MVC 4 Web应用程序". 选择"基本&q ...

  5. ASP.NET Web API实践系列03,路由模版, 路由惯例, 路由设置

    ASP.NET Web API的路由和ASP.NET MVC相似,也是把路由放在RouteTable中的.可以在App_Start文件夹中的WebApiConfig.cs中设置路由模版.默认的路由模版 ...

  6. ASP.NET Web API实践系列02,在MVC4下的一个实例, 包含EF Code First,依赖注入, Bootstrap等

    本篇体验在MVC4下,实现一个对Book信息的管理,包括增删查等,用到了EF Code First, 使用Unity进行依赖注入,前端使用Bootstrap美化.先上最终效果: →创建一个MVC4项目 ...

  7. ASP.NET Web API实践系列01,以ASP.NET Web Form方式寄宿

    创建一个空的ASP.NET Web Form项目. 右键项目,添加新项,创建Web API控制器类,TestController. 删除掉TestController默认的内容,编写如下: using ...

  8. ASP.NET Web API实践系列11,如何设计出优秀的API

    本篇摘自:InfoQ的微信公众号 在设计API的时候考虑的问题包括:API所使用的传输协议.支持的消息格式.接口的控制.名称.关联.次序,等等.我们很难始终作出正确的决策,很可能是在多次犯错之后,并从 ...

  9. ASP.NET Web API 2系列(三):查看WebAPI接口的详细说明及测试接口

    引言 前边两篇博客介绍了Web API的基本框架以及路由配置,这篇博客主要解决在前后端分离项目中,为前端人员提供详细接口说明的问题,主要是通过修改WebApi HelpPage相关代码和添加WebAp ...

随机推荐

  1. USB的挂起和唤醒(Suspend and Resume)【转】

    转自:http://m.blog.csdn.net/blog/luckywang1103/25244091 USB协议的第9章讲到USB可见设备状态[Universal Serial Bus Spec ...

  2. javascript-dom文档对象模型1

    HTML DOM (文档对象模型) 当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model). HTML DOM 模型被构造为对象的树. 一:HTML DOM 树 ...

  3. HTML5 drawImage性能问题

    最近做游戏发现,为了节省HTTP请求,把很多组动画放到了一张图片上,也就是传说中的雪碧图,但是用drawImage从图片上截取一部分绘制在canvas上时会非常慢. 我的图片尺寸为5000*7000 ...

  4. hdu 1394 求一个序列的最小逆序数 单点增 区间求和

    题目的意思就好比给出一个序列 如:0 3 4 1 2 设逆序数初始n = 0: 由于0后面没有比它小的,n = 0 3后面有1,2 n = 2 4后面有1,2,n = 2+2 = 4: 所以该序列逆序 ...

  5. js中call与apply用法

    call和apply,它们的作用都是将函数绑定到另外一个对象上去运行 两者的格式和参数定义: call( thisArg [,arg1,arg2,… ] ); // 参数列表,arg1,arg2,.. ...

  6. Dynamic Rankings || 动态/静态区间第k小(主席树)

    JYF大佬说,一星期要写很多篇博客才会有人看 但是我做题没有那么快啊QwQ Part1 写在前面 区间第K小问题一直是主席树经典题=w=今天的重点是动态区间第K小问题.静态问题要求查询一个区间内的第k ...

  7. 009.Zabbix幻灯片展示

    一 Slide shows简介 Slide shows在多个Screens之间以幻灯片的方式来展示,使一个屏幕可以显示多个页面. 二 Slide shows配置 2.1 创建多个Screen Slid ...

  8. grid网格系统布局

    grid布局: 基于网格的2维布局方法1:display: grid | inline-grid | subgrid作用:启用网格grid容器 grid:定义一个块级的网格容器 inline-grid ...

  9. 初识PHP(一)

    做为一名合格的前端开发攻城狮,了解一门服务端语言是必须的,所以我选了php.都说学的第一门语言对第二门语言会产生较大的影响,确实,每当我看到一个php知识点时,就同时会想到这个知识点在Javascri ...

  10. iOS 11开发教程(十三)iOS11应用编辑界面添加视图

    iOS 11开发教程(十三)iOS11应用编辑界面添加视图 在iOS中添加视图的方式有两种:一种是使用编辑界面添加视图:另一种是使用代码添加视图.以下是这两个方式的详细介绍. 1.编辑界面添加视图 使 ...