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

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

namespace System.Net.Http
{
    public abstract class HttpMessageHandler : IDisposable
    {
        protected HttpMessageHandler()
        {

        }

        protected internal abstract Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);

        protected virtual void Dispose(bool disposing)
        {

        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize((object)this);
        }
    }
}


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

public abstract class DelegatingHandler : HttpMessageHandler
{
    private HttpMessageHandler innerHandler;

    protected DelegatingHandler(HttpMessageHandler innerHandler)
    {
        this.innerHandler = innerHandler;
    }

    public HttpMessageHandler InnerHandler
    {
        get
        {
            return this.innerHandler;
        }
        set
        {
            ...
            this.innerHandler = value;
        }
    }

    protected internal override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if(request == null)
        {
            throw new ArgumentNullException("request");
        }
        ...
        return this.innerHandler.SendAsync(request, cancellationToken);
    }
}


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

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

public class HttpServer : DelegatingHandler
{
    public HttpConfiguration Configuration{get;}
    public HttpMessageHandler Dispatcher{get;}
    public HttpServer();
    public HttpServer(HttpMessageHandler dipatcher);
    public HttpServer(HttpConfiguration configuration);
    public HttpServer(HttpConfiguration configuration, HttpMessageHandler dispatcher);
    protected overrde void Dispose(bool disposing); //处理HttpConfiguration对象,因为该对象实现了IDisposable接口
    protected virutal void Initialize();
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cacellationToken);

    //别忘了,HttpServer的父类DelegatingHandler还有一个InnerHandler属性。
}


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

HttpConfiguration又是什么呢?

public class HttpConfiguration : IDisposable
{
    ...
    public Collection<DelegatingHandler> MessageHandlers{get;}
}

原来,我们可以从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的类。

using System;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace ControlAndRoute.Extension
{
    public class MethodOverrideHandler : DelegatingHandler
    {
        private readonly string[] _methods = {"DELETE", "HEAD", "PUT"};
        private const string _header = "X-HTTP-Method-Override";

        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (request.Method == HttpMethod.Post && request.Headers.Contains(_header))
            {
                //从请求头中获取X-HTTP-Method-Override的属性值
                var method = request.Headers.GetValues(_header).FirstOrDefault();
                if (_methods.Contains(method, StringComparer.InvariantCultureIgnoreCase))
                {
                    request.Method = new HttpMethod(method);
                }
            }
            return base.SendAsync(request, cancellationToken);
        }
    }
}


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

using System.Web.Http;
using ControlAndRoute.Extension;

namespace ControlAndRoute
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            //注册自定义的DelegatingHandler
            config.MessageHandlers.Add(new MethodOverrideHandler());

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            // 取消注释下面的代码行可对具有 IQueryable 或 IQueryable<T> 返回类型的操作启用查询支持。
            // 若要避免处理意外查询或恶意查询,请使用 QueryableAttribute 上的验证设置来验证传入查询。
            // 有关详细信息,请访问 http://go.microsoft.com/fwlink/?LinkId=279712。
            //config.EnableQuerySupport();

            // 若要在应用程序中禁用跟踪,请注释掉或删除以下代码行
            // 有关详细信息,请参阅: http://www.asp.net/web-api
            config.EnableSystemDiagnosticsTracing();
        }
    }
}


在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. 修改history记录数门限

    你的 Bash 命令历史保存的历史命令的数量可以在 ~/.bashrc 文件里设置.在这个文件里,你可以找到下面两行: HISTSIZE=1000 HISTFILESIZE=2000 HISTSIZE ...

  2. mysql5.7 主从复制的正常切换【转】

    目前环境如下: master server IP:172.17.61.131 slave server IP:172.17.61.132 mysql version: mysql-5.7.21-lin ...

  3. 003_cd pushd popd三个命令的区别

    一. It depends. In zsh you can configure cd to push the old directory on the directory stack automati ...

  4. mysql及linux发行版下载源

    MySQL国内镜像资源 搜狐开源镜像站:http://mirrors.sohu.com/ 国内开源镜像站点汇总 http://segmentfault.com/a/1190000000375848   ...

  5. LR开发接口脚本

    char token_id,tenant_id;    web_add_header("Accept","application/json");    web_ ...

  6. MongoDB aggregate 运用篇(转)

    http://www.cnblogs.com/qq78292959/p/4440679.html 最近一直在用mongodb,有时候会需要用到统计,在网上查了一些资料,最适合用的就是用aggregat ...

  7. mybatis generator 双击创建失败

    https://coding.imooc.com/learn/questiondetail/20460.html 用的下面这个方法

  8. 浅谈HIbernate

    Hiberbate是面向对象,需要把对象和数据库进行映射.与数据库无关,操作的是对象,会根据数据源和数据库的方言生成对应的sql语句进行查询,是一个优秀的java持久层解决方案,是当今主流的对象-关系 ...

  9. 【转】卖萌的大牛你桑不起啊 ——记CVPR2011一篇极品文章

    来源:http://blog.renren.com/share/228707015/7197269922 作者 : 庞宇 CVPR2011正在如火如荼的进行中,在网上能看到的部分文章中,我终于找到一篇 ...

  10. 【LOJ】#2479. 「九省联考 2018」制胡窜

    题解 老了,国赛之前敲一个后缀树上LCT和线段树都休闲的很 现在后缀树上线段树合并差点把我写死 主要思路就是后缀树+线段树合并+容斥,我相信熟练的OIer看到这已经会了 但就是不想写 但是由于我过于老 ...