[Web API] Web API 2 深入系列(1) 路由
目录
-
ASP.NET 路由
-
注册路由
-
动态映射HttpHandler
-
-
WebAPI 路由
-
注册路由
-
调用GetRouteData
-
-
2个路由系统衔接
-
GlobalConfiguration
-
HostedHttpRoute
-
-
补充
路由是进入Web API的第一扇门.目的用于确定Controller名称、Action名称、路由参数.
ASP.NET 路由
注册路由
在ASP.NET中注册路由的方式:
RouteCollection.MapPageRoute()
添加1个完整的路由:
var defaults = new RouteValueDictionary//路由变量默认值
{
{"code","010"},
{"phone","1000000"},
};
var constraints = new RouteValueDictionary//路由变量约束
{
{"code",@"0\d{2,3}" },
{"phone",@"\d{7,9}" },
{"httpMethod",new HttpMethodConstraint("POST") }
};
var dataTokens = new RouteValueDictionary//路由相关参数,不用于处理路由匹配功能
{
{"defaultCode","北京" },
{"defaultPhone","北京X电话" }
};
RouteTable.Routes.MapPageRoute("default", "{code}/{phone}", "~/call.aspx", false, defaults, constraints, dataTokens);
获取RouteCollection
一般通过RouteTable.Routes
public class RouteTable
{
private static RouteCollection _instance = new RouteCollection();
public static RouteCollection Routes { get { return RouteTable._instance; } }
}
路由:RouteBase
public abstract class RouteBase
{
public bool RouteExistingFiles {get; set;} = true;//对现有文件进行路由
public abstract RouteData GetRouteData(HttpContextBase httpContext);
public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
}
路由默认实现:Route
public class Route : RouteBase
{
/// <summary>
/// 路由约束
/// </summary>
public RouteValueDictionary Constraints { get; set; }
/// <summary>
/// 路由自定义参数(一般存储备注说明等)
/// </summary>
public RouteValueDictionary DataTokens { get; set; }
/// <summary>
/// 路由变量默认值
/// </summary>
public RouteValueDictionary Defaults { get; set; }
/// <summary>
/// 路由对应处理程序对象
/// </summary>
public IRouteHandler RouteHandler { get; set; }
/// <summary>
/// 路由模板
/// </summary>
public string Url { get; set; }
}
忽略路由:
忽略路由本质是添加一个StopRoutingHandler(返回空的HttpHandler)的路由
routes.Ignore("010/1000001");(路由先注册,先匹配)
routes.MapPageRoute("default", "{code}/{phone}", "~/call.aspx", false, defaults, constraints, dataTokens);
路由约束:
在上面完整的路由添加Demo中,路由约束有2种方式
- 正则表达式字符串
- 实现IRouteConstraint接口
动态映射HttpHandler
路由是通过UrlRoutingModule这个HttpModule实现动态拦截.
public virtual void PostResolveRequestCache(HttpContextBase context)
{
RouteData routeData = RouteTable.Routes.GetRouteData(context);
IRouteHandler routeHandler = routeData.RouteHandler;
IHttpHandler httpHandler = routeHandler.GetHttpHandler(new RequestContext(context, routeData));
context.RemapHandler(httpHandler);
}
WebAPI 路由
注册路由
在Web API中注册路由的方式:HttpRouteCollection.MapHttpRoute(),
这里重点看下MapHttpRoute方法内部
public static class HttpRouteCollectionExtensions
{
public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints, HttpMessageHandler handler)
{
IHttpRoute route = routes.CreateRoute(routeTemplate, (IDictionary<string, object>)defaults, (IDictionary<string, object>)constraints, (IDictionary<string, object>)null, handler);
routes.Add(name, route);
return route;
}
}
获取HttpRouteCollection
在WebAPI中通过HttpConfiguration的Routes属性
public class HttpConfiguration : IDisposable
{
public Collection<DelegatingHandler> MessageHandlers { get; }
public HttpRouteCollection Routes { get; }
public ConcurrentDictionary<object, object> Properties { get; }
}
WebAPI路由:IHttpRoute
public interface IHttpRoute
{
string RouteTemplate { get; }
IDictionary<string, object> Defaults { get; }
IDictionary<string, object> Constraints { get; }
IDictionary<string, object> DataTokens { get; }
HttpMessageHandler Handler { get; }
IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestMessage request);
IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary<string, object> values);
}
WebAPI路由默认实现:HttpRoute
由于IHttpRoute设计的非常全,HttpRoute基本就是实现了IHttpRoute.
(这种设计上比RouteBase要优势很多)
调用GetRouteData
与ASP.NET稍微不同的是
- 直接对所有请求进行路由 而不再判断是否文件存在.
- 添加一个VirtualPathRoot的过滤
public class HttpRouteCollection
{
public virtual IHttpRouteData GetRouteData(HttpRequestMessage request)
{
string virtualPathRoot = request.GetRequestContext().VirtualPathRoot;
IHttpRouteData routeData = this._collection[index].GetRouteData(virtualPathRoot, request);
return routeData;
}
}
2个路由系统衔接
Web API提供了一套自身的路由系统,所以不依赖于ASP.NET.
寄宿方式有多种.如果以WebHost的方式.本质上还是走的ASP.NET路由处理.
GlobalConfiguration
WebAPI的配置在HttpConfiguration中提供.
在WebHost中,定义了GlobalConfiguration用来创建HttpConfiguration
public static class GlobalConfiguration
{
public static HttpConfiguration Configuration = GlobalConfiguration.CreateConfiguration();//创建HttpConfiguration
public static HttpMessageHandler DefaultHandler = GlobalConfiguration.CreateDefaultHandler();//创建HttpRoutingDispatcher(WebAPI管道尾部HttpMessageHandler)
public static HttpServer DefaultServer = GlobalConfiguration.CreateDefaultServer();//创建HttpServer(WebAPI管道开头HttpMessageHandler)
public static void Configure(Action<HttpConfiguration> configurationCallback)//提供方便配置路由
{
configurationCallback(GlobalConfiguration.Configuration);
}
private static Lazy<HttpConfiguration> CreateConfiguration()
{
return new Lazy<HttpConfiguration>((Func<HttpConfiguration>) (() =>
{
//这里用HostedHttpRouteCollection实现HttpRouteCollection
return new HttpConfiguration((HttpRouteCollection) new HostedHttpRouteCollection(RouteTable.Routes));
}));
}
}
HostedHttpRoute
在GlobalConfiguration中创建的HttpConfiguration对象是用HostedHttpRouteCollection作为参数
这里我觉得有必要看下CreateRoute和Add方法
internal class HostedHttpRouteCollection : HttpRouteCollection
{
//真正维护的路由集合
private readonly RouteCollection _routeCollection;
//创建路由 MapHttpRoute会调用该方法
public override IHttpRoute CreateRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler)
{
return (IHttpRoute) new HostedHttpRoute(uriTemplate, defaults, constraints, dataTokens, handler);
}
public override void Add(string name, IHttpRoute route)
{
//只是添加到内部的routeCollection中
//route.ToRoute() => route.OriginalRoute;
this._routeCollection.Add(name, (RouteBase) route.ToRoute());
}
public override IHttpRouteData GetRouteData(HttpRequestMessage request)
{
//通过调用内部的Route的GetRouteData
RouteData routeData = this._routeCollection.GetRouteData(httpContextBase);
return (IHttpRouteData)new HostedHttpRouteData(routeData);
}
}
而HostedHttpRoute在WebHost中
相当于用HttpRoute的身 却提供了真正的Route
internal class HostedHttpRoute : IHttpRoute
{
//ASP.NET Route
internal Route OriginalRoute { get; private set; }
public HostedHttpRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler)
{
//内部的OriginalRoute实际为HttpWebRoute
this.OriginalRoute = (Route) new HttpWebRoute(uriTemplate, defaults1, constraints1, dataTokens1, <strong>(IRouteHandler) HttpControllerRouteHandler.Instance</strong>, (IHttpRoute) this);
this.Handler = handler;
}
}
从HostedHttpRoute构造函数中 我们看到了真正的Route为HttpWebRoute 且RouteHandler为HttpControllerRouteHandler.Instance
public class HttpControllerRouteHandler : IRouteHandler
{
public static HttpControllerRouteHandler Instance{ get { return new HttpControllerRouteHandler(); } }
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return (IHttpHandler) new HttpControllerHandler(requestContext.RouteData);
}
}
补充
冗余的设计:
-
HttpRoute中Handler的HttpMessageHandler,未发现有任何地方使用到.
-
HttpRoute和Route中DataTokens,建议直接取消.
备注:
-
文章中的代码并非完整,一般是经过自己精简后的.
-
本篇内容使用MarkDown语法编辑