Web API源码笔记-路由信息

  • A+
所属分类:.NET技术
摘要

由于之前分析过详细的源码单步解析,所以这里不再每一步探究具体的细节,而是上升一个层面以总结和回顾的形式来看待
在webapi中是采用先将路由和处理程序注册,在处理请求时根据路由信息找到处理程序,注册流程如下


1.路由基本介绍

由于之前分析过详细的源码单步解析,所以这里不再每一步探究具体的细节,而是上升一个层面以总结和回顾的形式来看待
在webapi中是采用先将路由和处理程序注册,在处理请求时根据路由信息找到处理程序,注册流程如下
Web API源码笔记-路由信息

一个web应用有一个全局的路由表,并通过RouteTable.Route来表示,它的返回值类型是一个继承自Collection的RouteCollection类型,在应用初始化时将路由规则直接或者间接的添加到路由表中。

1.Web Form中的路由绑定

web Form中添加路由是最直观的阐述了上面的介绍,因为在webform中我们直接使用RouteCollection类型的实例方法MapPageRoute就可以了,而在web API应用中是有点不一样的。

var Routes = RouteTable.Routes; Routes.MapPageRoute("", "Test/{name}/{id}","~/default.aspx", true, defaults); 
2.Web API中的路由绑定

WEB Api注册路由入口

 public static void Register(HttpConfiguration config)  {      config.Routes.MapHttpRoute(          name: "DefaultApi",          routeTemplate: "api/{controller}/{id}",          defaults: new { id = RouteParameter.Optional }      );      }    protected void Application_Start()  {      GlobalConfiguration.Configure(WebApiConfig.Register);  } 

这里的config.Routes就是一个HttpRouteCollection对象

在webapi中绑定路由,其实最终也是绑定到全局路由表RouteTable上,但是过程却稍微间接一些,是通过一个类型为HttpRouteCollection或者继承自它的实例扩展方法来进行绑定的.

1.首先在webapi应用初始化时默认创建了继承自HttpRouteCollection类型的HostedHttpRouteCollection的实例并将全局路由表RouteTable.Routes传入

2.返回一个HttpConfiguration的实例config作为WebApiConfig.Register的参数,然后调用扩展方法来添加路由到RouteTable上,当然其中有一些细节是不一样的,目前先省略

3.顺便说一下包括路由注册在内对整个Web API管道的配置都是通过HttpConfiguration来完成的,一切可以从GlobalConfiguration静态类中找到答案

3.HostedHttpRouteCollection

由于web API框架是一个抽象的消息处理管道,具有自己的路由系统,并且它是由一系列注册实现了IHttpRoute接口的HttpRoute对象组成

1.在GlobalConfiguration中的CreateConfiguration方法返回的我们可以看到HttpConfiguration包含的类型是一个HostedHttpRouteCollection类型.

 private static Lazy<HttpConfiguration> CreateConfiguration()  {     return new Lazy<HttpConfiguration>(() =>        {            HttpConfiguration config = null;            var newInstance = new HostedHttpRouteCollection(RouteTable.Routes);            config = new HttpConfiguration(newInstance);            return config;        }); } 

2.包含在HostedHttpRouteCollection之中的创建出的Route类型是一个HostedHttpRoute

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); }      

3.在HostedHttpRouteCollection中有一个RouteCollection类型的字段_routeCollection 并且在初始化时将RouteTable.Routes传递给了它

public HostedHttpRouteCollection(RouteCollection routeCollection)     : this(routeCollection, virtualPathRoot: null) { } public HostedHttpRouteCollection(RouteCollection routeCollection, string virtualPathRoot) {     _routeCollection = routeCollection;     _virtualPathRoot = virtualPathRoot; } 

4.在类中重写了Add方法,它会将指定的HttpRoute对象转为Route,并且添加到ASP.NET的全局路由表中,到这里说明在webapi webHost中依然还是采用的ASP.NET路由,只不过在实现上做了一些调整

 public override void Add(string name, IHttpRoute route)  {      _routeCollection.Add(name, route.ToRoute());  } 
4.HttpControllerRouteHanlder和HttpControllerHanlder

1.通过原来的学习我们知道,在ASP .NET中整个路由的核心是UrlRoutingModule的HttpModule,在UrlRoutingModule中根据请求上下文得到当前路由的路由处理程序IRouteHandler

UrlRoutingModule处理代码

 public virtual void PostResolveRequestCache(HttpContextBase context)  {      //根据请求获取注册的路由信息      RouteData routeData = RouteCollection.GetRouteData(context);      //获取到路由信息的路由处理程序      IRouteHandler routeHandler = routeData.RouteHandler;      RequestContext requestContext = new RequestContext(context, routeData);      context.Request.RequestContext = requestContext;      //由路由处理程序得到具体处理的HttpHanlder      IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);  } 

2.此时得到的是一个HttpControllerRouteHandler对象的路由处理程序,然后在根据路由处理程序获取到HttpControllerHandler,最终处理由HttpControllerHandler中的ProcessRequestAsyncCore方法处理,也同时标志此时请求将正式被WEB Api管道接管开始执行

 internal async Task ProcessRequestAsyncCore(HttpContextBase contextBase)  {     HttpRequestMessage request = contextBase.GetHttpRequestMessage() ?? ConvertRequest(contextBase);      HttpResponseMessage response = null;      try     {         //开始进入管道        response = await _server.SendAsync(request, cancellationToken);                    }     catch (OperationCanceledException)     {                 }    finally    {               } }