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管道

Web Host消息处理管道的更多相关文章

  1. Web APi之Web Host消息处理管道(六)

    前言 我们知道Web API本身是无法提供请求-响应的机制,它是通过Web Host以及Self Host的寄宿的宿主方式来提供一个请求-响应的运行环境.二者都是将请求和响应抽象成HttpRespon ...

  2. ASP.NET Web API消息处理管道:Self Host下的消息处理管道[下篇]

    ASP.NET Web API消息处理管道:Self Host下的消息处理管道[下篇] 我们知道ASP.NET Web API借助于HttpSelfHostServer以Self Host模式寄宿于当 ...

  3. 细说Asp.Net Web API消息处理管道(二)

    在细说Asp.Net Web API消息处理管道这篇文章中,通过翻看源码和实例验证的方式,我们知道了Asp.Net Web API消息处理管道的组成类型以及Asp.Net Web API是如何创建消息 ...

  4. ASP.NET Web API的消息处理管道: Self Host下的消息处理管道[上篇]

    ASP.NET Web API的消息处理管道: Self Host下的消息处理管道[上篇] ASP.NET Web API服务端框架核心是一个独立于具体寄宿环境的消息处理管道,它不关心请求消息来源于何 ...

  5. ASP.NET Web API 框架研究 Self Host模式下的消息处理管道

    Self Host模式下的ASP.NET Web API与WCF非常相似,都可以寄宿在任意类型的托管应用程序中,宿主可以是Windows Form .WPF.控制台应用以及Windows Servic ...

  6. ASP.NET Web API 框架研究 Web Host模式下的消息处理管道

    寄宿的作用是开启一个进程为Web API提供一个运行环境以解决持续监听.请求监听和响应回复,即将接收到的请求转换成HttpRequestMessage对象传入管道,并将管道生成并经过处理后的HttpR ...

  7. ASP.NET Web API 框架研究 Web Host模式路由及将请求转出到消息处理管道

    Web Host 模式下的路由本质上还是通过ASP.NET 路由系统来进行路由的,只是通过继承和组合的方式对ASP.NET路由系统的内部的类进行了一些封装,产生自己专用一套类结构,功能逻辑基本都是一样 ...

  8. ASP.NET Web API的消息处理管道: HttpRoutingDispatcher

    ASP.NET Web API的消息处理管道: HttpRoutingDispatcher 认情况下,作为消息处理管道“龙头”的HttpServer的Dispatcher属性返回一个HttpRouti ...

  9. ASP.NET Web API的消息处理管道:"龙头"HttpServer

    ASP.NET Web API的消息处理管道:"龙头"HttpServer 一般来说,对于构成ASP.NET Web API消息处理管道的所有HttpMessageHandler来 ...

随机推荐

  1. macbook连接linuxserver后不显示matlab桌面[问题]

    macbook 登录远程linuxserver.Macbook今天,系统版本号10.9.4.这是更新,打开matlab当提醒x11不存在.一个接着安装Xquarz2.7.6. matlab它可以在本地 ...

  2. 编C语言单元测试框架CUnit方法库

    /*********************************************************************  * Author  : Samson  * Date   ...

  3. 添加xml文件编辑语法提示

    找到Struts的lib目录 找到struts2-core-文件并解压开 这个struts.dtd文件才是我们需要添加的文件 双击XML Catalog 点击ADD Key中复制粘贴D:\web\st ...

  4. poj 2586 Y2K Accounting Bug (贪心)

    Y2K Accounting Bug Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8678   Accepted: 428 ...

  5. LoadImage()使用

    该系统被定义: WINUSERAPIHANDLEWINAPILoadImageA(    HINSTANCE,    LPCSTR,    UINT,    int,    int,    UINT) ...

  6. c++学习笔记4,调用派生类的顺序构造和析构函数(一个)

    测试源代码: //測试派生类的构造函数的调用顺序何时调用 //Fedora20 gcc version=4.8.2 #include <iostream> using namespace ...

  7. C++笔试面试总结

    手游广州某公司书面今天接受采访时.刚进去中午1中场休息.他们公司谁刚刚醒来,一个冷漠打牌,然后去上班.瞬间,这些公司有没有什么好印象,压抑. 接着快2点的时候.发了一份笔试题.大部分题目均在网上的&l ...

  8. Java正则表达式例子汇总

    1.过滤特殊字符 package com.sheepmu.text; /* * @author sheepmu */ public class HWCompetition { public stati ...

  9. Fire Net HDU

    Fire Net Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other) Total Subm ...

  10. 程序员联盟有自己的论坛啦!基于Discuz构建,还不来注册~

    我把程序员联盟网站的论坛建好了,哈哈哈.用的是Discuz这个腾讯旗下的中文bbs建设软件.正在完善论坛,添加各种模块和应用.大家可以先去注册一下:coderunity.com/bbs/forum.p ...