Web APi之Web Host消息处理管道

Posted Jeffcky

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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对象,我们看看该对象的定义:

Web

其中最重要的是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对象的定义:

Web

当请求过来时请求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管道​​】

 Web

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


你所看到的并非事物本身,而是经过诠释后所赋予的意义


以上是关于Web APi之Web Host消息处理管道的主要内容,如果未能解决你的问题,请参考以下文章

Web API 之SelfHost与OwinSelfHots加载外部程序

Web API框架学习——消息管道

Web API源码剖析之HttpServer

ASP.NET web api汇总

Docker 之web api 访问 host sql server

Self Host web api并从单元测试项目访问它