zoukankan      html  css  js  c++  java
  • Web Host消息处理管道

    Web Host消息处理管道

    前言

    我们知道Web API本身是无法提供请求-响应的机制,它是通过Web Host以及Self Host的寄宿的宿主方式来提供一个请求-响应的运行环境。二者都是将请求和响应抽象成HttpResponseMessage和HttpRequesMessage对象,并将请求HttpRequestMessage传入到HttpMessageHandler进行处理最终将响应通过HttpResponseMessage逆向通过HttpMessageHandler返回到客户端,但是在其过程中,此二者在管道中的机制是不一样的,由于在最新Web API中是以Web Host寄宿实现,所以仅本节仅讨论Web Host寄宿的实现。

    Web Host 

    Web API采用Web Host寄宿模式,其路由系统最终还是通过ASP.NET的路由系统来实现,也就是说其本质是将ASP.NET应用程序作为Web API的宿主,利用ASP.NET自身的路由系统并结合IIS来实现去持续监听以及请求和响应的问题。

    既然Web Host寄宿是利用ASP.NET的路由系统实现,那么我们首先就得了解下ASP.NET的路由系统,ASP.NET的路由是利用UrlRoutingModel中的HttpModel来完成,并通过HttpApplication中的PostResloveRequesCache事件对其请求进行拦截,并借助注册的路由将请求的URL进行解析获得路由数据对象RouteData,UrlRoutingModel最终从匹配对象Route对象中获取对应的HttpHandler并映射给当前上下文HttpContext,紧接着上下文HttpContext获取请求并进行响应。

    上述听起来似乎有点拗口并且不太能让人理解,我们首先来了解下ASP.NET的请求管道,当一个请求过来时首先会经过扩展名来进行相应的处理,如果是静态文件则直接返回到客户端,若是动态文件如扩展名为.aspx,则将交给aspnet.isapi.dll来进行处理,然后就是创建一个ASP.NET运行环境HttpRuntime,在创建运行环境的同时首先创建一个HttpWorkerRquest对象,这个对象则保存着请求的报文信息,接着借助HttpWorkerRequest对象创建上下文 HttpContext (包含着HttpResponse和HttpRequest以及HttpSessionState等)接下来就是通过ApplicationFactory工厂创建一个上述所说的 HttpApplication 对象,此对象为管道事件的核心对象,然后调用ProcessRequest方法将上下文HttpContext传入其中,最终通过HttpApplication来执行19个管道事件,在前八个事件利用 HttpModel 进行相关的验证、授权等,在第八个事件则创建页面类对象同时实现IHttpHandler接口创建一个我们上述所说的 HttpHandler ,后面大概就是创建页面控件树以及执行页面对象的页面生命周期等。

    通过上述叙述知,Web API利用Web Host作为寄宿则利用UrlRoutingModel来动态映射给HttpContext中的HttpHandler是实现管道集成的核心,它会将ASP.NET中的HttpRequest对象表示的请求转换成HttpRequestMessage对象并传入到消息管道中,并将输出的HttpResponseMessage写入到HttpResponse中并最终进行响应输出。现在最主要的问题是这个HttpHandler在Web Host模式下是怎样实现的?请继续往下看。

    HttpControllerRoutingHandler

    当通过Web API的配置文件通过模板以及约束等注册一个HttpRoute路由对象到路由集合中时也就是将其注册到路由表中(因为路由表中的属性Routes存着注册的路由的路由集合),首先会创建一个HostedHttpRoute对象,我们看看该对象的定义:

    其中最重要的是OriginalRoute属性,照定义来看就仅仅是创建了这个对象而已,确确实实是这样,因为该对象仅仅是起一个过渡作用,因为真正创建的对象是HttpWebRoute的Route对象,通过查看其构造函数可知,如下:

    复制代码
    public HostedHttpRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler)
    {
        RouteValueDictionary dictionary;
        RouteValueDictionary dictionary2;
        RouteValueDictionary dictionary3;
        base..ctor();
        dictionary = (defaults != null) ? new RouteValueDictionary(defaults) : null;
        dictionary2 = (constraints != null) ? new RouteValueDictionary(constraints) : null;
        dictionary3 = (dataTokens != null) ? new RouteValueDictionary(dataTokens) : null;
        this.OriginalRoute = new HttpWebRoute(uriTemplate, dictionary, dictionary2, dictionary3, HttpControllerRouteHandler.Instance, this);
        this.Handler = handler;
        return;
    }
    复制代码

    如上红色标记,此时将属性OriginalRoute作为HttpWebRoute对象的引用存在HostedHttpRoute中并返回一个Route对象。所以在注册路由的过程是:

    HttpRoute-------------->HostedHttpRoute-------------->HttpWebRoute------------->Route

    在HttpWebRoute继承自Route类中有一个属性RouteHandler如下:

    public IRouteHandler RouteHandler { [CompilerGenerated] get; [CompilerGenerated] set; }

    我们通过如下HttpControllerRouteHandler的定义知,上述RouteHandler就是一个HttpControllerRouteHandler对象,因为HttpControllerRouteHandler实现了接口IRouteHandler,如下:

    复制代码
    public class HttpControllerRouteHandler : IRouteHandler
    {
        // Fields
        private static readonly Lazy<HttpControllerRouteHandler> _instance;
        [CompilerGenerated]
        private static Func<HttpControllerRouteHandler> CS$<>9__CachedAnonymousMethodDelegate1;
    
        // Methods
        static HttpControllerRouteHandler();
        protected HttpControllerRouteHandler();
        [CompilerGenerated]
        private static HttpControllerRouteHandler <.cctor>b__0();
        protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext);
        IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext);
    
        // Properties
        public static HttpControllerRouteHandler Instance { get; }
    }
    复制代码

    注意上述标记,通过GetHttpHandler方法知,在ASP.NET路由中的HttpHandler就是由HttpControllerRouteHandler对象提供的,同时我们看下该方法的定义:

    protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        return new HttpControllerHandler(requestContext.RouteData);
    }

    看见这个方法的返回值没,又涉及到一个对象,其实通过HttpControllerRouteHandler中的方法获得的HttpHandler其实是一个HttpControllerHandler对象,而整个消息处理管道则是有它来创建的。请继续往下看!

    HttpControllerHandler

    上面说过此对象是Web Host寄宿模式下的整个管道的执行者,下面我们就来看看这个类的定义(操蛋,太长了还是就看下这个类的继承,再叙述其他的再给代码):

    public class HttpControllerHandler : HttpTaskAsyncHandler
    {
    }

    再看下其父类的定义:

    复制代码
    public abstract class HttpTaskAsyncHandler : IHttpAsyncHandler, IHttpHandler
    {
        // Methods
        protected HttpTaskAsyncHandler();
        [EditorBrowsable(EditorBrowsableState.Never)]
        public virtual void ProcessRequest(HttpContext context);
        public abstract Task ProcessRequestAsync(HttpContext context);
        IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData);
        void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result);
    
        // Properties
        public virtual bool IsReusable { get; }
    }
    复制代码

    简单叙述下,HttpRouteHandler是HttpTaskAsyncHadler的子类,并且其父类为实现了IHttpAsync和IHttpHandler接口的抽象类。最主要的是HttpTaskAsyncHandler调用了实现IHttpAsyncHandler接口的BeginProcessRequest方法,并返回一个IAsyncResult,而EndProcessRequest方法则用来执行了返回值为IAsyncResult的BeginProcessRequest方法。

    我们再来看看HttpRouteHandler的一个构造函数:

     public HttpControllerHandler(RouteData routeData, HttpMessageHandler handler);

    该构造函数的第一个参数就是根据路由解析得到的路由数据,第二个参数就是管道中的处理程序,实质上就是第一个HttpMessageHandler即管道头(HttpServer)。

    下面我们总结下在ASP.NET路由系统中HttpControllerHandler被创建的整个过程。请看!

     总结

    当进入ASP.NET请求管道中时,在HttpModel中通过事件对其请求进行拦截后,然后利用UrlRoutingModel中注册的路由对象对当前请求的URL进行匹配,若匹配通过由对应匹配的路由生成一个RouteData对象,当然这个Route对象就是HttpWebRoute对象,接着利用RoteData对象对应的Route来获得RouteHandler,这个RouteHandler就是HttpControllerRouteHandler,接着利用UrlRoutingModel中得到的RouteData和当前上下文HttpContext生成一个请求上下文对象,再以该请求上下文对象为对象调用HttpControllerRouteHandler上的GetHttpHandler方法获得HttpHandler(返回的是HttpControllerHandler),并将HttpHandler映射到当前上下文HttpContext中,然后调用HttpControllerHandler上继承自IHttpAsyncHandler上的BeginProcessRequest方法开始进入Web API管道。

    下面我们实现IHttpModel接口并对照上述叙述进行编码,如下:

    复制代码
        public class WebHost : IHttpModule
        {
    
            public void Dispose()
            {
                throw new NotImplementedException();
            }
    
            public void Init(HttpApplication context)
            {
                context.PostResolveRequestCache += context_PostResolveRequestCache;
            }
    
            void context_PostResolveRequestCache(object sender, EventArgs e)
            {
                var app = sender as HttpApplication;
                var contextWrapper = new HttpContextWrapper(app.Context);
                var routeData = RouteTable.Routes.GetRouteData(contextWrapper);
                var requestContext = new RequestContext(contextWrapper, routeData);
                var httpHandler = routeData.RouteHandler.GetHttpHandler(requestContext);
                var httpAsyncHandler = httpHandler as IHttpAsyncHandler;
                httpAsyncHandler.BeginProcessRequest(app.Context, null, null);
            }
        }
    复制代码

    总结

    下面就Web API中Web Host寄宿模式下的消息处理管道给出整体示意图:来自【Web Host管道

     

  • 相关阅读:
    JS DataURL 整理(一)
    JavaScript 与 ECMAScript 的关系
    HTML5 多媒体之<svg>标签 使用
    HTML5 多媒体之<canvas>标签 使用
    [转]信号量---进程间通信 ---php版
    [转]使用PHP的ftok()函数实现基于linux下系统级进程间消息通信demo(消息队列模式)
    【转】php ftok 使用
    [转]Golang调度模型
    【转】如何分析golang程序的内存使用情况---很有用
    【转】Go 中对栈中函数进行内联---这篇讲的不如上一篇,也还可以吧。。
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4836948.html
Copyright © 2011-2022 走看看