dotnet core 開發體驗之Routing

開始

回顧上一篇文章:dotnet core開發體驗之開始MVC 裏面體驗了一把mvc,而後咱們知道了aspnet mvc是靠Routing來驅動起來的,因此感受須要研究一下Routing是什麼鬼。html

Routing簡單使用體驗

首先咱們用命令yo aspnet建立一個新的空web項目。(Yeoman的使用本身研究,參考:https://docs.asp.net/en/latest/client-side/yeoman.html?#building-projects-with-yeomangit

建立完項目後,在project.json裏面添加Routing依賴。github

"dependencies": {
    ...
    "Microsoft.AspNetCore.Routing": "1.0.0-*"
},

添加完依賴後,修改Startup裏面的Configure,和ConfigureServices裏面添加Routing的使用依賴
修改前:web

public void Configure(IApplicationBuilder app)
{
        app.Run(async (context) =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
}

修改後:json

public void ConfigureServices(IServiceCollection services)
{
        services.AddRouting();
}

public void Configure(IApplicationBuilder app)
{
        var endpoint = new RouteHandler((c) => c.Response.WriteAsync("Hello, I am Routing!"));

        app.UseRouter(endpoint);
}

dotnet run 而後瀏覽器訪問http://localhost:5000/ 顯示爲Hello, I am Routing! 接下來咱們在http://localhost:5000/ 後面加入一些其餘的東西來訪問,發現其實仍是同樣打印Hello, I am Routing! 這讓咱們感受好像並無什麼卵用的樣子。不着急咱們先來看看Routing是怎麼運行起來的。在開始這話題以前須要先了解到什麼是中間件,參考:https://docs.asp.net/en/latest/fundamentals/middleware.html瀏覽器

Routing的最基本運行原理

Routing的驅動入口就是基於middleware的。能夠先看看 app.UseRouter(endpoint)的內部實現,參考一個擴展方法類RoutingBuilderExtensions,能夠看到最後有一句代碼return builder.UseMiddleware<RouterMiddleware>(router) 。這裏能夠很明顯看到,入口在RouterMiddleware的Invoke方法。mvc

public async Task Invoke(HttpContext httpContext)
    {
        var context = new RouteContext(httpContext);
        context.RouteData.Routers.Add(_router);

        await _router.RouteAsync(context);

        if (context.Handler == null)
        {
            _logger.RequestDidNotMatchRoutes();
            await _next.Invoke(httpContext);
        }
        else
        {
            httpContext.Features[typeof(IRoutingFeature)] = new RoutingFeature()
            {
                RouteData = context.RouteData,
            };

            await context.Handler(context.HttpContext);
        }
    }

這個入口的實現是這樣的:app

Invoke入口 ===>>> 實例化一個 RouteContext ===>>> 把咱們傳進來的 IRouter 存到 RouteContext裏面的 RouteData ===>>> 再執行IRouter的RouteAsync方法把RouteContext對象傳進去提供給具體實現使用。===>>> 若是context.Handler沒有東西,就執行下一個RequestDelegate,若是有的話就把RouteData保存起來而後執行這個RequestDelegate。asp.net

看到了這裏咱們已經能夠明白下面這個代碼運行起來的原理。async

public void Configure(IApplicationBuilder app)
{
        var endpoint = new RouteHandler((c) => c.Response.WriteAsync("Hello, I am Routing!"));

        app.UseRouter(endpoint);
}

可能還會有人不明白RouteHandler是個什麼鬼,既然咱們知道了代碼的實現運行原理,那麼確定能夠猜到RouteHandler是有實現接口IRouter的。咱們能夠看RouteHandler代碼

public Task RouteAsync(RouteContext context)
    {
        context.Handler = _requestDelegate;
        return TaskCache.CompletedTask;
    }

這裏能夠看到把咱們的(c) => c.Response.WriteAsync("Hello, I am Routing!")賦值給context.Handler,而後由RouterMiddleware來執行咱們這個事件方法。因而咱們就能夠在瀏覽器上面看到輸出 Hello, I am Routing!這麼一句話了。

Routing的一些使用

文章到如今,咱們雖然知道了Routing運行起來的一個大概原理,可是咱們一直打印出相同內容,確實也沒有什麼卵用呀。咱們要改一下讓打印內容能有點改變。這個時候可使用到Routing提供的Route類來使用。代碼修改以下:

public void Configure(IApplicationBuilder app)
    {
        var endpoint = new RouteHandler((c) => c.Response.WriteAsync($"Hello, I am Routing! your item is {c.GetRouteValue("item")}"));
        var resolver = app.ApplicationServices.GetRequiredService<IInlineConstraintResolver>();
        var runRoute = new Route(endpoint,"{item}",resolver);

        app.UseRouter(runRoute);
    }

修改完代碼後,咱們再次編譯運行,而後輸入http://localhost:5000/ 咱們發現一片空白,而後再輸入http://localhost:5000/abc 發現打印出來的是Hello, I am Routing! your item is abc。而後再輸入其餘的 http://localhost:5000/abc/cc 發現也是一片空白。這是由於咱們給路由添加的匹配是主機地址/+{item}那其餘的路徑都是匹配不到,那麼確定就是不會顯示任何東西啦。假設咱們要給一個默認值,那麼能夠改爲這樣

var runRoute = new Route(endpoint,"{item=home}",resolver);

OK,這個時候咱們再輸入http://localhost:5000/ 看到的就是Hello, I am Routing! your item is home。

匹配原理相對比較複雜點,想要了解的話能夠參考 RouteBase的源碼,而後看相關的類,看看咱們設置的模板是如何解析的,而後如何和url進行匹配的。若是要要來解釋完整個過程的話,這個文章確定是不夠的,因此各位能夠本身瞭解一下。

假如要配置多個路由支持的話,可使用RouteCollection

public void Configure(IApplicationBuilder app)
    {
        var endpoint = new RouteHandler((c) => c.Response.WriteAsync($"Hello, I am Routing! your item is {c.GetRouteValue("item")}"));
        var resolver = app.ApplicationServices.GetRequiredService<IInlineConstraintResolver>();
        var runRoute = new Route(endpoint,"{item=home}",resolver);
        var otherRoute = new Route(endpoint,"other/{item=other_home}",resolver);

        var routeCollection = new RouteCollection();
        routeCollection.Add(runRoute);
        routeCollection.Add(otherRoute);

        app.UseRouter(routeCollection);
    }

修改爲上面的代碼後就支持兩個路由,假如輸入的url是 http://localhost:5000/other 那麼就是使用runRoute,若是輸入的是http://localhost:5000/other/myother 那麼使用的就是otherRoute。

這樣書寫暴露了不少細節東西,咱們能夠用 Routing提供的RouteBuilder類來編寫相同的東西。代碼修改一下以下:

public void Configure(IApplicationBuilder app)
    {
        var endpoint = new RouteHandler((c) => c.Response.WriteAsync($"Hello, I am Routing! your item is {c.GetRouteValue("item")}"));
        
        var routeBuilder = new RouteBuilder(app)
        {
            DefaultHandler = endpoint,
        };
        
        routeBuilder.MapRoute("default","{item=home}");
        routeBuilder.MapRoute("other","other/{item=other_home}");


        app.UseRouter(routeBuilder.Build());
    }

若是有一些特殊的的路由配置,咱們也可使用routeBuilder.Routes.Add(route);這代碼來添加。至於能配置的模板都有些什麼,能夠看 Routing 的 Template 的測試類:https://github.com/aspnet/Routing/tree/dev/test/Microsoft.AspNetCore.Routing.Tests/Template 看完基本就知道都有些什麼樣的模板格式可使用了。

實現本身的 RouteHandler

到如今,咱們已經知道了Routing大概是怎麼運行起來,知道了如何簡單的使用。那麼接下來能夠來建立一個本身的RouteHandler,來加深一下對Routing的使用體驗。

建立一個類MyRouteHandler,實現接口IRoute:

public class MyRouteHandler : IRouter
{
    public VirtualPathData GetVirtualPath(VirtualPathContext context)
    {
        return null;
    }

    public Task RouteAsync(RouteContext context)
    {

        context.Handler = (c) =>
        {
            var printStr = $"controller:{c.GetRouteValue("controller")}," +
            $"action:{c.GetRouteValue("action")},id:{c.GetRouteValue("id")}";

            return c.Response.WriteAsync(printStr);
        };
        return TaskCache.CompletedTask;
    }
}

而後咱們的路由配置改爲這樣:

public void Configure(IApplicationBuilder app)
    {
        var endpoint = new MyRouteHandler();
        
        var routeBuilder = new RouteBuilder(app)
        {
            DefaultHandler = endpoint,
        };
        
        routeBuilder.MapRoute("default","{controller=Home}/{action=Index}/{id?}");

        app.UseRouter(routeBuilder.Build());
    }

而後打開瀏覽器http://localhost:5000/ 打印出來的內容是 controller:Home,action:Index,id: 。這樣是否是很像咱們去調用mvc的控制器和控制器的行爲呢?Routing的體驗文章到這來就結束了,謝謝觀看。


因爲本人水平有限,知識有限,文章不免會有錯誤,歡迎你們指正。若是有什麼問題也歡迎你們回覆交流。要是你以爲本文還能夠,那麼點擊一下推薦。

相關文章
相關標籤/搜索