我們知道任何asp.net web程序的處理都是由IHttpHandler來實現的,那么這里我看看web api是如何讓獲取IHttpHandler的。這里假設你已經能熟練的使用web api,我還是沿用以前的風格以一個簡單的demo來說明吧。默認在我們的Global.asax.cs有這么一句
WebApiConfig.Register(GlobalConfiguration.Configuration);而WebApiConfig.Register的默認實現也很簡單:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
一看這個方法我們就知道這是在注冊一個路由信息,同時也提示我們以后自己開發asp.net mvc的時候,盡量把web api的路由分來處理,這里單獨放到WebApiConfig.Register來處理的。
首先我們還是來看看這里的HttpConfiguration是個什么東東,在GlobalConfiguration中有這么一句
HttpConfiguration config = new HttpConfiguration(new HostedHttpRouteCollection(RouteTable.Routes));
其中這里用到的HostedHttpRouteCollection、HttpConfiguration構造函數如下:
private readonly RouteCollection _routeCollection;
public HostedHttpRouteCollection(RouteCollection routeCollection)
{
if (routeCollection == null)
{
throw Error.ArgumentNull("routeCollection");
}
_routeCollection = routeCollection;
}
public HttpConfiguration(HttpRouteCollection routes)
{
if (routes == null)
{
throw Error.ArgumentNull("routes");
}
_routes = routes;
Services = new DefaultServices(this);
ParameterBindingRules = DefaultActionValueBinder.GetDefaultParameterBinders();
}
到這里我們可以知道HttpConfiguration是可以操作路由的,里面有一個 public HttpRouteCollection Routes屬性。
現在我們來看看路由究竟是怎么添加的,MapHttpRoute方法具體實現如下:
public static IHttpRoute MapHttpRoute(this HttpRouteCollection 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);
IHttpRoute route = routes.CreateRoute(routeTemplate, defaultsDictionary, constraintsDictionary, dataTokens: null, handler: handler);
routes.Add(name, route);
return route;
}
}
這里的routes是HostedHttpRouteCollection實例,其CreateRoute方法實現如下:
public override IHttpRoute CreateRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler)
{
return new HostedHttpRoute(uriTemplate, defaults, constraints, dataTokens, handler);
}
其中HostedHttpRoute的構造函數如下:
public HostedHttpRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler)
{
RouteValueDictionary routeDefaults = defaults != null ? new RouteValueDictionary(defaults) : null;
RouteValueDictionary routeConstraints = constraints != null ? new RouteValueDictionary(constraints) : null;
RouteValueDictionary routeDataTokens = dataTokens != null ? new RouteValueDictionary(dataTokens) : null;
OriginalRoute = new HttpWebRoute(uriTemplate, routeDefaults, routeConstraints, routeDataTokens, HttpControllerRouteHandler.Instance, this);
Handler = handler;
}
HostedHttpRoute的OriginalRoute屬性有點特殊,是一個HttpWebRoute實例,HttpWebRoute的構造函數如下:
internal class HttpWebRoute : Route
public HttpWebRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler, IHttpRoute httpRoute)
: base(url, defaults, constraints, dataTokens, routeHandler)
{
if (httpRoute == null)
{
throw Error.ArgumentNull("httpRoute");
}
HttpRoute = httpRoute;
}
我們知道路由信息表里面的實例都是Route,看來這個的HttpWebRoute實例是我們真正需要的路由信息。這個路由處理的handler是 HttpControllerRouteHandler,其中HttpControllerRouteHandler實現了一個單例模式:
public class HttpControllerRouteHandler : IRouteHandler
{
private static readonly Lazy<HttpControllerRouteHandler> _instance =
new Lazy<HttpControllerRouteHandler>(() => new HttpControllerRouteHandler(), isThreadSafe: true);
public static HttpControllerRouteHandler Instance
{
get { return _instance.Value; }
}
}
現在我們回到MapHttpRoute方法中來,這里已經獲取到了一個IHttpRoute的實例(HostedHttpRoute實例),現在剩下就只有一句了 routes.Add(name, route);,它的實現如下:
public override void Add(string name, IHttpRoute route)
{
_routeCollection.Add(name, route.ToRoute());
}
在HostedHttpRouteCollection的構造函數中曾把 _routeCollection設置為RouteTable.Routes,這里實際是在往RouteTable.Routes添加路由信息。這里的 route的IHttpRoute方法實現如下:
public static Route ToRoute(this IHttpRoute httpRoute)
{
if (httpRoute == null)
{
throw Error.ArgumentNull("httpRoute");
}
HostedHttpRoute hostedHttpRoute = httpRoute as HostedHttpRoute;
if (hostedHttpRoute != null)
{
return hostedHttpRoute.OriginalRoute;
}
return new HttpWebRoute(
httpRoute.RouteTemplate,
MakeRouteValueDictionary(httpRoute.Defaults),
MakeRouteValueDictionary(httpRoute.Constraints),
MakeRouteValueDictionary(httpRoute.DataTokens),
HttpControllerRouteHandler.Instance,
httpRoute);
}
到這里我們應該知道web api 默認的Route其實就是HttpWebRoute,其對應的IRouteHandler默認是HttpControllerRouteHandler,至於程序是如何通過路由信息表來找到路由這里我們就忽略了,大家可以參考
asp.net mvc源碼分析-路由篇 如何找到 IHttpHandler
asp.net mvc源碼分析-Route的GetRouteData
找到了IRouteHandler那么后面就調用它的GetHttpHandler來獲取IHttpHandler實例,這里HttpControllerRouteHandler的GetHttpHandler實現非常簡單:
return new HttpControllerHandler(requestContext.RouteData);
那么我們來看看HttpControllerHandler的構造函數:
public class HttpControllerHandler : IHttpAsyncHandler
public HttpControllerHandler(RouteData routeData)
{
if (routeData == null)
{
throw Error.ArgumentNull("routeData");
}
_routeData = new HostedHttpRouteData(routeData);
}
而HostedHttpRouteData的構造函數如下:
public HostedHttpRouteData(RouteData routeData)
{
if (routeData == null)
{
throw Error.ArgumentNull("routeData");
}
OriginalRouteData = routeData;
HttpWebRoute route = routeData.Route as HttpWebRoute;
Route = route == null ? null : route.HttpRoute;
}
至於這里的HostedHttpRoute 為什么需要一個OriginalRoute ,HostedHttpRouteData 需要一個OriginalRouteData 屬性,在這里我就不多說了吧,相信大家應該都知道他們的作用吧,在后面需要的地方我再說說這2個屬性干什么東東了。
這里我們還是總結一下吧:web api默認的路由都是在WebApiConfig.Register方法中添加,默認的route是HttpWebRoute實例,默認的 IRouteHandler是HttpControllerRouteHandler實例,它返回的handler是一個實現 IHttpAsyncHandler接口HttpControllerHandler實例。