1. 程式人生 > >ASP.NET Core 2.1 : 十五.圖解路由(2.1 or earler)

ASP.NET Core 2.1 : 十五.圖解路由(2.1 or earler)

本文通過一張圖來看一下路由的配置以及請求處理的機制,。

一、概述

路由主要有兩個主要功能:

  1. 將請求的URL與已定義的路由進行匹配,找到該URL對應的處理程式並傳入該請求進行處理。
  2. 根據已定義的路由生成URL

這兩個功能看起來這兩個是相反的。

A.路由的配置

路由的兩個功能都離不開一個基本的操作:路由的基本配置。在Startup中預設通過 routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}")定義,

當然我們還可以繼續 routes.MapRoute(。。。);  這樣就定義了一系列的路由匹配方式組成一個路由表,例如這樣:

app.UseMvc(routes =>
{
    routes.MapRoute(name: "test", template: "Hello");
    routes.MapRoute("flylolo/{code}/{name}", MyRouteHandler.Handler);
    routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}");
});

每一個MapRoute會生成一個Route,第二個MapRoute看起來有些特殊,我們可以傳入一個自定義的RequestDelegate(本例為MyRouteHandler.Handler)來處理“flylolo/{code}/{name}”這樣的請求,

    public static class MyRouteHandler
    {
        public static async Task Handler(HttpContext context)
        {
            await context.Response.WriteAsync("MyRouteHandler");
        }
    }

它會被封裝成一個RouteHandler(new RouteHandler(MyRouteHandler.Handler))賦值給Route的target屬性,而對於另外兩種沒有指定的,Route的target屬性預設會被指定為MvcRouteHandler

,如下圖:

B.Handler的選擇

當請求進入之後,根據此路由表對該URL進行逐一匹配,並將請求交給匹配到的路由的target(即MvcRouteHandler或RouteHandler),呼叫 _target.RouteAsync(context); ,在這個方法中,若是MvcRouteHandler會對請求的Controller和Action驗證,若驗證成功,則對context(是一個RouteContext)的Handler屬性賦值一個匿名方法;若是RouteHandler則會直接將其封裝的RequestDelegate(本例為MyRouteHandler.Handler)賦值給RouteContext.Handler.

C.請求處理

經過Handler的選擇後,若RouteContext.Handler不為空,則呼叫RouteContext.Handler(HttpContext)對請求進行處理。

D.其他

回想一下中介軟體,這個是不是和app.Map("/test", XXHandle)這樣配置中介軟體的方式有點像,當請求路徑是/test的時候,請求交由XXHandle處理,同樣是Map,對比著更容易理解。

下面通過一張圖看一下路由配置和請求處理的流程。

二、流程及解析

 點選看大圖

  為了方便檢視,對幾個“重點物件”做了顏色標識(點選圖片可以看大圖):

 

  1. 路由的初始化配置
  2. 一切從Startup開始,之前在中介軟體的文章中介紹過,一般是通過多個UseXXX的方式將多箇中間件組成“請求處理管道”,而在這裡通過UseMvc方法進行配置,傳入routes.MapRoute(...)這樣的一個或多個配置。
      接下來會New一個

RouteBuilder

    ,顧名思義就是一個Route的建立者,通過呼叫傳進來的一個或多個routes.MapRoute()方法生成多個Route,並配置預設的Handler。
                var routes = new RouteBuilder(app)
                {
                    DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(),
                };

                configureRoutes(routes);//呼叫Startup中的routes.MapRoute(...)方法

 

          ①呼叫RouteBuilder的Build方法,生成一個RouteCollection

        public IRouter Build()
        {
            var routeCollection = new RouteCollection();

            foreach (var route in Routes)
            {
                routeCollection.Add(route);
            }

            return routeCollection;
        }

 

          ②RouteCollection實現IRouteCollection和IRouter介面,他是在Startup中的配置組成的集合。

          ③RouterMiddleWare就是專門用於進行路由處理的中介軟體,在此將RouteCollection作為中介軟體RouterMiddleWare的引數,並將這個中介軟體插入管道中。

    public class RouterMiddleware
    {
        private readonly IRouter _router; //就是RouteCollection

        public async Task Invoke(HttpContext httpContext);

    }

 

  2. 請求處理流程

        ④請求的處理流程在RouterMiddleWare的invoke()方法中。

      ⑤請求首先會被封裝成一個RouteContext,本質就是將httpContext、_router(也就是RouteCollection)包裝到一個物件裡。

  var context = new RouteContext(httpContext);
  context.RouteData.Routers.Add(_router);
public class RouteContext
{
   private RouteData _routeData;
   public RequestDelegate Handler ;
   public HttpContext HttpContext;//簡單的賦值
   public RouteData RouteData;
}

          ⑥呼叫_router(也就是RouteCollection)的RouteAsync(context)方法,在其中遍歷每一個路由

          ⑦若與請求URL匹配,則將對應的Handler賦值給context.Handler。

        public async virtual Task RouteAsync(RouteContext context)
        {
            // 快照備份
            var snapshot = context.RouteData.PushState(null, values: null, dataTokens: null);
            //遍歷
            for (var i = 0; i < Count; i++)
            {
                var route = this[i];
                context.RouteData.Routers.Add(route);

                try
                {
                    await route.RouteAsync(context);//若匹配,則給context.Handler賦值

                    if (context.Handler != null)
                    {
                        break;
                    }
                }
                finally
                {
                    if (context.Handler == null)
                    {
                        snapshot.Restore();//通過快照還原
                    }
                }
            }
        }

              ⑧在RouterMiddleWare的invoke()方法中,呼叫新賦值的context.Handler處理HttpContext;

httpContext.Features[typeof(IRoutingFeature)] = new RoutingFeature()
{
    RouteData = context.RouteData,
};

await context.Handler(context.HttpContext);

 三、其他

      由於文章寫的比較早各種原因一直沒有寫完,現在發現2.2版本之後,啟用了新的路由方案,還是把這章完成了發出來,有願意看的可以參考一下,下一篇文章介紹一下2.2版的新的路由方案,至於通過路由生成URL部分,就暫時不寫了。