zoukankan      html  css  js  c++  java
  • ASP.NET Web API实践系列05,消息处理管道

    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自定义DelegatingHandlerHttpControllerDispatcher

     

    举例:重新设置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,输入如下:

    1

     

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

    2

     

  • 相关阅读:
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    软件工程实践总结
  • 原文地址:https://www.cnblogs.com/darrenji/p/4056159.html
Copyright © 2011-2022 走看看