上一節描述了API的整個運作架構,即分為三層hosting、message handler pipeline 和 controller handling。此節講其中一個宿主,WebHost 寄宿在asp.net 傳統管道上。
Routing(路由)
在asp.net平台,路由是一般由RouteTables.Routes靜态屬性添加的,類型是RouteCollection,例如下面的MVC模闆自帶的添加路由的代碼。
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
大部分路由邏輯都是在UrlRoutingModule裡,屬于PostResolveRequestCache asp.net管道事件。每一次請求,這個module都是重新比對一次這個路由集合,并擷取一個RouteData執行個體,如果比對,則:
1、執行個體化RouteData同時擷取一個route handler(HttpControllerRouteHandler)。
2、從routehandler擷取一個http handle 繼承自IHttphandler接口。IRouteHandler接口方法:IHttpHandler GetHttpHandler(RequestContext requestContext)
3、最後,目前的請求上下文(RequestContext)被映射到上面的這個httphandler。
是以,最後結果是asp.net管道請求被此handler處理。
Web API 內建
當寄宿在asp.net上,Web API特定的配置被定義在一個單例模式的HttpConfiguration對象裡,通過靜态屬性GlobalCnfiguration.Configuration通路。
Web API 也定義了一對新的RouteCollection擴充方法MapHttpRoute,來注冊Web API特殊的路由,下面是配置的例子代碼:
HttpConfiguration config = GlobalConfiguration.Configuration;
config.Routes.MapHttpRoute("default", "{controller}/{id}", new {id = UrlParameter.Optional});
// other configuration settings
注意:
- 靜态屬性GlobalConfiguration.Configuration是用于擷取配置的引用,内部這個對象指向的是全局的RouteTables.Routes這個集合。
- 當新增路由時使用的是MapHttpRoute這個擴充方法。
當一個route通過MapHttpRoute增加後比對到一個Request,,HttpControllerRouteHandler會建立一個新的HttpControllerHandler,其繼承自IAsyncHttpHandler,此handler通過RouteData(包含了路由的資訊)初始化。
當被調用,HttpControllerhandler 在他的BeginReocessRequest方法裡有以下行為:
- 為目前上下文建立HttpRequestMessage執行個體
- 使用GlobalConfiguration.Configuration擷取配置并建立一個HttpServer,并且把HttpRequestMessage送入服務管道。
當這個請求被HttpServer接受後,進入宿主的獨立處理階段(Web API的新管道)
下面的類圖是路由解決過程的摘要,并且配置設定給HttpServer(消息處理管道)

原文位址:
ASP.NET Web API: web hosting
看文字描述比較晦澀難懂,需要根據源碼了解,上圖容易了解。
HttpApplication -> UrlRoutingModule -> RouteCollection -> RouteData -> HttpControllerRouteHandler -> HttpControllerHandler -> HttpRequestMessage -> HttpServer
附加一些源碼:
1、UrlRoutingModule擷取RouteData
public virtual void PostResolveRequestCache(HttpContextBase context)
{
RouteData routeData = this.RouteCollection.GetRouteData(context);
if (routeData == null)
{
return;
}
IRouteHandler routeHandler = routeData.RouteHandler;
if (routeHandler == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
}
if (routeHandler is StopRoutingHandler)
{
return;
}
RequestContext requestContext = new RequestContext(context, routeData);
context.Request.RequestContext = requestContext;
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
if (httpHandler == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[]
{
routeHandler.GetType()
}));
}
if (!(httpHandler is UrlAuthFailureHandler))
{
context.RemapHandler(httpHandler);
return;
}
if (FormsAuthenticationModule.FormsAuthRequired)
{
UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
return;
}
throw new HttpException(401, SR.GetString("Assess_Denied_Description3"));
}
2、MapHttpRoute(RouteCollectionExtension)
public static Route MapHttpRoute(this RouteCollection routes, string name, string routeTemplate, object defaults, object constraints, HttpMessageHandler handler)
{
if (routes == null)
{
throw Error.ArgumentNull("routes");
}
HttpRouteValueDictionary defaultsDictionary = new HttpRouteValueDictionary(defaults);
HttpRouteValueDictionary constraintsDictionary = new HttpRouteValueDictionary(constraints);
HostedHttpRoute httpRoute = (HostedHttpRoute)GlobalConfiguration.Configuration.Routes.CreateRoute(routeTemplate, defaultsDictionary, constraintsDictionary, dataTokens: null, handler: handler);
Route route = httpRoute.OriginalRoute;
routes.Add(name, route);
return route;
}
3、HttpControllerRouteHandler.GetHandler
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new HttpControllerHandler(requestContext.RouteData);
}
4、HttpControllerHandler.ProcessRequest
internal async Task ProcessRequestAsyncCore(HttpContextBase contextBase)
{
HttpRequestMessage request = contextBase.GetHttpRequestMessage() ?? ConvertRequest(contextBase);
// Add route data
request.SetRouteData(_routeData);
CancellationToken cancellationToken = contextBase.Response.GetClientDisconnectedTokenWhenFixed();
HttpResponseMessage response = null;
try
{
response = await _server.SendAsync(request, cancellationToken);
await CopyResponseAsync(contextBase, request, response, _exceptionLogger.Value, _exceptionHandler.Value,
cancellationToken);
}
finally
{
// The other HttpTaskAsyncHandler is HttpRouteExceptionHandler; it has similar cleanup logic.
request.DisposeRequestResources();
request.Dispose();
if (response != null)
{
response.Dispose();
}
}
}
5、HttpApplication -> HttpControllerHandler
public HttpControllerHandler(RouteData routeData)
: this(routeData, GlobalConfiguration.DefaultServer)//HttpServer : DelegatingHandler : HttpMessageHandler
{
}
public HttpControllerHandler(RouteData routeData, HttpMessageHandler handler)
{
if (routeData == null)
{
throw Error.ArgumentNull("routeData");
}
if (handler == null)
{
throw Error.ArgumentNull("handler");
}
_routeData = new HostedHttpRouteData(routeData);
_server = new HttpMessageInvoker(handler);
}