zoukankan      html  css  js  c++  java
  • Web APi之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模式下是怎样实现的?请继续往下看。

    HttpControllerRouteHandler

    当通过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对象。

    那么上述所说的RouteData是什么时候生成呢?

    我们再来看看这个Route对象的定义:

    当请求过来时请求URL会与注册的路由(Route)进行匹配,若匹配成功,由上知,此时同时会通过GetRouteData方法生成一个 RouteData 对象,我们同时看看该RouteData的定义:

    public class RouteData
    {
        // Fields
        private RouteValueDictionary _dataTokens;
        private IRouteHandler _routeHandler;
        private RouteValueDictionary _values;
    
        // Methods
        public RouteData();
        public RouteData(RouteBase route, IRouteHandler routeHandler);
        public string GetRequiredString(string valueName);
    
        // Properties
        public RouteValueDictionary DataTokens { get; }
        public RouteBase Route { get; set; }
        public IRouteHandler RouteHandler { get; set; }
        public RouteValueDictionary Values { get; }
    }

    此对象中有一个匹配路由Route的引用属性,也就是当前匹配成功的Route对象并且还有返回值为IRouteHandler的属性RouteHandler。

    所以在注册路由的过程是:

    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对象,接着利用RouteData对象对应的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管道

     

    温馨提示 :有关上述图片HttpControllerDispatcher在后续原理中进行讲解。。。。。。

  • 相关阅读:
    MSN无法登录(错误代码80072745)的解决方法
    C#3.0新体验(二) 扩展方法
    My DreamTech
    让IE崩溃的bug, IE8也一样崩溃
    多线程的相关概念
    10条PHP经验总结
    PHP框架 CI与TP之MVC比较
    多线程设计要点
    Linux yum命令的使用技巧
    BigPipe 的工作原理
  • 原文地址:https://www.cnblogs.com/CreateMyself/p/4834785.html
Copyright © 2011-2022 走看看