1. 程式人生 > >ASP.NET MVC , ASP.NET Web API 的路由系統與 ASP.NET 的路由系統是怎麽銜接的?

ASP.NET MVC , ASP.NET Web API 的路由系統與 ASP.NET 的路由系統是怎麽銜接的?

動態 nstat 而已 cache 講解 routing enc stat 靜態屬性

ASP.NET MVC 的路由實際上是建立在 ASP.NET 的路由系統之上的.

MVC 路由註冊通常是這樣的: 技術分享圖片

RouteTable 是一個全局路由表, 它的 Routes 靜態屬性是一個 RouteCollection 類型的實例,而 RouteCollection 是一個繼承自 Collection<RouteBase> 的子類, RouteBase 是 ASP.NET 路由系統定義的基類

.技術分享圖片

RouteBase 有一個唯一的實現類:

技術分享圖片

當我們通過如下方法註冊一個路由時:

技術分享圖片

實際是向全局路由表中添加了一個 Route 類型的實例,部分源碼如下:

    public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
    {
      ......
      Route route = new Route(url, (IRouteHandler) new MvcRouteHandler())
      {
    ......
      };
    ......
      routes.Add(name, (RouteBase) route);
      return route;
    }

從源碼中我們可以看到,添加 Route 對象的時候,直接傳入了一個 MvcRouteHandler 類型的實例.

我們知道, ASP.NET 的路由系統對路由的解析是通過一個註冊的 HttpModule 對象實現對請求的攔截,然後為當前 Http 上下文動態映射一個 HttpHandler 對象, 而這個 HttpHandler 對象會接管對當前請求的處理並最終對請求予以響應.

這個註冊的 HttpModule 對象的類型叫做 UrlRoutingModule .

我們可以在ASP.NET 的全局系統配置文件中找到它:

     <httpModules>
        ......
            <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule"/>
           ......
        </httpModules>

該類型在 PostResolveRequestCache 事件實現對請求的攔截:

技術分享圖片

在攔截時,它做了這麽幾件事(部分源碼省略):

    public virtual void PostResolveRequestCache(HttpContextBase context)
    {
      RouteData routeData = this.RouteCollection.GetRouteData(context);     
      IRouteHandler routeHandler = routeData.RouteHandler;
      IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
      context.RemapHandler(httpHandler);
    }

1.遍歷所有註冊的路由,也就是所有添加到全局路由表中的 Route 類型的實例,通過調用它們的 GetRouteData 方法,拿到第一個匹配的 RouteData (路由數據);

2.拿到路由數據中的 RouteHandler 對象, 其實就是 MvcRouteHandler 類型的實例;

3.調用 MvcRouteHandler 的 GetHttpHandler 方法,拿到 HttpHandler.

MvcRouteHandler 的 GetHttpHandler 方法源碼如下:

    protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
      requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));
      return (IHttpHandler) new MvcHandler(requestContext);
    }

可以看到,直接 new 了一個 MvcHandler 類型的實例,

最終,請求轉交給這個 MvcHandler 類型的實例處理.

ASP.NET Web API 是怎麽與 ASP.NET 路由系統接軌的呢?

我們知道, ASP.NET 的路由系統對路由的解析是通過一個註冊的 HttpModule 對象實現對請求的攔截,然後為當前 Http 上下文動態映射一個 HttpHandler 對象, 而這個 HttpHandler 對象會接管對當前請求的處理並最終對請求予以響應.

這一條不僅對 MVC 適用, 對 Web API 同樣適用,因為他倆都是借助於 ASP.NET 的路由系統.

區別在於 HttpHandler 的類型不一樣而已.

ASP.NET Web API 註冊路由的代碼通常是這樣的:

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new
                {
                    id = RouteParameter.Optional
                }
            );

config.Routes 是一個 HttpRouteCollection 類型的實例,並且是只讀的.

只讀的,就意味著只能在該實例所屬的類的構造函數中初始化.

我們知道,這個 config 是 HttpConfiguration 類型,它在 GlobalConfiguration 類中初始化.

在它的初始化代碼中,我們可以看到:

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

HttpConfiguration 實際是對 HostedHttpRouteCollection 的封裝,而後者是對 RouteTable.Route 的封裝. 即 ASP.NET 全局路由表的封裝.

所以說, HttpConfiguration 類型封裝了 ASP.NET 的全局路由表. 它的 Routes 屬性的實際類型是 HostedHttpRouteCollection

我們再回頭看 config.Routes.MapHttpRoute 方法 , 也就是 HostedHttpRouteCollection 類型的 MapHttpRoute 方法:

        public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints, HttpMessageHandler handler)
        {
            ......
            IHttpRoute route = routes.CreateRoute(routeTemplate, defaultsDictionary, constraintsDictionary, dataTokens: null, handler: handler);
            routes.Add(name, route);
            return route;
        }

很簡單,創建了一個路由,然後添加它.

我們繼續查看 HostedHttpRouteCollection 類型的 CreateRoute 方法:

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

返回了一個 HostedHttpRoute 類型的實例.

我們可以把這個方法 和 上面 MVC 的 MapRoute 方法做比較:

MVC:

    public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
    {
      ......
      Route route = new Route(url, (IRouteHandler) new MvcRouteHandler())
      {
    ......
      };
    ......
      routes.Add(name, (RouteBase) route);
      return route;
    }

是不是非常像!不同的只是 MVC new 的路由對象是 Route 類型,而 Web API new 的路由對象是 HostedHttpRoute 類型.

講到這裏,其實 ASP.NET Web API 的路由系統還沒有和 ASP.NET 的路由系統銜接起來,它們二者的關系僅僅體現在下面這句話:

HttpConfiguration 實際是對 HostedHttpRouteCollection 的封裝,而後者是對 RouteTable.Route 的封裝. 即 ASP.NET 全局路由表的封裝.

但是,當 HostedHttpRoute 創建後,調用 HostedHttpRouteCollection 的 Add 方法添加時,銜接就真正開始了:

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

_routeCollection 是 RouteCollection 類型,沒看錯,就是 ASP.NET 路由系統的 RouteCollection .

所以,這句代碼實際是向 ASP.NET 路由系統的路由集合中添加路由,目的就是為了讓 UrlRoutingModule 能夠攔截到匹配了 Web API 註冊的路由的請求.

但是,問題來了,從上面 MVC 的講解中我們知道, ASP.NET 路由系統的 RouteCollection 是一個繼承自 Collection<RouteBase> 的子類, RouteBase 是 ASP.NET 路由的基類,

而 HostedHttpRoute 是實現了 IHttpRoute 接口的實例,

IHttpRoute 和 RouteBase 風馬牛不相接啊!

所以,添加時,Web API 通過 HostedHttpRoute 的 ToRoute 方法,將自己轉成了 RouteBase 類型!!

這個轉化非常簡單:

 public static Route ToRoute(this IHttpRoute httpRoute)
        {
       ...... HostedHttpRoute hostedHttpRoute = httpRoute as HostedHttpRoute; if (hostedHttpRoute != null) { return hostedHttpRoute.OriginalRoute; }
       ...... }

問題又來了, HostedHttpRoute 類型的 OriginalRoute 是個什麽鬼?當然,肯定是個 Route 類型,也就是說,它是一個 ASP.NET 路由系統定義的 Route 類型.那它是怎麽來的呢?

我們知道,在 Web API 註冊路由時, MapHttpRoute 內部創建了一個 HostedHttpRoute 類型的實例,並且是直接 new 的.

那麽我們去看看 HostedHttpRoute 的構造函數:

        public HostedHttpRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler)
        {
            ......
            OriginalRoute = new HttpWebRoute(uriTemplate, routeDefaults, routeConstraints, routeDataTokens, HttpControllerRouteHandler.Instance, this);
            ......
        }

OriginalRoute 原來是一個 HttpWebRoute 類型,而 HttpWebRoute 則是 ASP.NET 路由系統定義的 Route 類型的子類.

技術分享圖片

並且,創建 HttpWebRoute 類型的實例時,傳入了一個 ASP.NET 路由系統定義的 IRouteHandler 類型的實例 : HttpControllerRouteHandler.Instance

而 HttpControllerRouteHandler 的 GetHttpHandler 方法如下:

        /// <summary>
        /// Provides the object that processes the request.
        /// </summary>
        /// <param name="requestContext">An object that encapsulates information about the request.</param>
        /// <returns>
        /// An object that processes the request.
        /// </returns>
        protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new HttpControllerHandler(requestContext.RouteData);
        }

返回了一個 HttpControllerHandler 類型的實例.

HttpControllerHandler 類型的XML註釋則非常清晰的解釋了它的作用:

/// 用於將 ASP.NET 請求傳遞給管道並寫回結果。</summary>

這裏說的管道,自然就是 Web API 的消息處理管道了.

總結:

ASP.NET MVC 和 ASP.NET Web API 都是通過 UrlRoutingModule ,在 PostResolveRequestCache 事件實現對請求的攔截.

攔截後,通過對HTTP上下文,路由等一系列處理後,

MVC 創建了 MvcHandler 進行具體的請求處理及響應;

Web API 創建了 HttpControllerHandler 進行具體的請求處理及響應.

ASP.NET MVC , ASP.NET Web API 的路由系統與 ASP.NET 的路由系統是怎麽銜接的?