我们知道任何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;
}
}
到这里我们可以知道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
{
return new HostedHttpRoute(uriTemplate, defaults, constraints, dataTokens, handler);
}
其中HostedHttpRoute的构造函数如下:
public HostedHttpRoute(string uriTemplate, IDictionary
{
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
new Lazy
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实例。