我是從.net 4.5直接跳到.net core 3.x的,感受asp.net這套東西最初是從4.5中的owin造成的。
目前官方文檔重點是講路由,沒有特別說明與傳統路由的區別,本篇主要介紹終結點路由的相關概念和如何使用,不會詳細介紹路由,這個參考官方文檔就ok了。若是未來有機會研究到底層再深度剖析。web
參考:
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/routing?view=aspnetcore-3.1
https://q.cnblogs.com/q/113644/
https://aregcode.com/blog/2019/dotnetcore-understanding-aspnet-endpoint-routing/api
最初咱們訪問 http://www.abc.com/a.aspx時,服務端是存在a.aspx這個文件的,服務端根據此文件幫咱們建立一個對應類的實例處理請求。
後來需求愈來愈複雜,出現了路由,目的是將請求地址與執行請求的處理器的直接關聯,變成映射關聯,映射規則由咱們本身配置。
在asp.net core 3.x以前這個路由系統是包含在mvc內部的,.net framework時代有個特殊的HttpModule來實現mvc,路由系統也包含其中。.net core是由有個特殊的中間件來實現mvc的,路由系統就包含在這個中間件中。
這種方式有個問題,mvc只是一箇中間件,路由系統包含在其中,若是咱們但願在mvc中間件以後加入其它中間件,其它中間件是沒法(也許是不方便)訪問路由相關信息的。
另外asp.net core並非只有mvc,還有webapi、blazor、signlR、接入gRpc等,未來還有更多,咱們的路由系統可否提出來,讓全部框架均可以用?mvc
所以出現了終結點路由,咱們說路由的根本目的是將用戶請求地址,映射爲一個請求處理器,最簡單的請求處理器能夠是一個委託 Func<HttpCotnext,Task>,也能夠是mvc/webapi中某個controller的某個action,因此從抽象的角度講 一個終結點 就是一個處理請求的委託。因爲mvc中action上還有不少attribute,所以咱們的終結點還應該提供一個集合,用來存儲與此請求處理委託的關聯數據。
從抽象的角度能夠簡單理解爲 一個終結點 = 處理請求的委託 + 與之關聯的附加(元)數據。對應到mvc來理解的話 終結點 = action + 應用其上的attribute集合。但記住終結點是個抽象的概念,並不僅服務於mvc,原理大概以下:app
在經過vs默認模板建立asp.net core 3.x項目時,在startup中會看到這樣的代碼框架
1 app.UseRouting(); 2 app.UseEndpoints(endpoints => { 3 endpoints.MapControllerRoute( 4 name: "default", 5 pattern: "{controller=Home}/{action=Index}/{id?}"); 6 });
看代碼的第2行。它有以下3個任務asp.net
這裏路由跟之前的寫法差很少,上面默認值啊、約束啊就去看官方文檔吧。
建立終結點也會參照屬性路由,微軟推薦webapi使用屬性路由,mvc使用傳統路由。你會看到建立默認webapi項目時這樣的 endpoints.MapControllers(); ide
默認狀況下是根據定義的路由去找到匹配的action最後生成終結點,這個生成終結點的過程咱們是能夠參與的,具體辦法是經過endpoints.MapControllerRoute的返回對象上調用相關擴展方法,本質上是向終結點的建立過程加入一些委託,未來建立終結點時,這些委託將被調用,代碼以下:ui
1 endpoints.MapControllerRoute( 2 name: "default", 3 pattern: "{controller=Home}/{action=Index}/{id?}").Add(endpointBuilder=> { 4 //經過endpointBuilder獲取與action關聯的數據,好比attribute和其它元數據 5 //經過endpointBuilder插入咱們向放進終結點的數據 6 });
app.UseEndpointsmvc時就說明了使用mvc和webapi了,默認狀況下一個action會建立一個對應的終結點,請求抵達時匹配到終結點就直接執行了。但有時候咱們但願本身控制一個請求過來時使用哪一個controller的哪一個action,具體作法:
定義一個類,繼承DynamicRouteValueTransformer,並註冊到ioc容器中,最後調用一個擴展方法,看代碼:url
1 class MyRouteValueTransformer : DynamicRouteValueTransformer 2 { 3 public override ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values) 4 { 5 //經過values能夠拿到原始路由數據 6 //能夠替換或加入新的數據 7 values.Add("controller", "jj"); 8 values.Add("action", "kkk"); 9 return new ValueTask<RouteValueDictionary>(values); 10 } 11 } 12 13 public void ConfigureServices(IServiceCollection services) 14 { 15 services.AddSingleton<MyRouteValueTransformer>(); 16 services.AddControllers(); 17 } 18 19 endpoints.MapDynamicControllerRoute<MyRouteValueTransformer>("aaa/bbb/{id}");
這樣未來請求抵達時,解析獲得終結點時會調用咱們的MyRouteValueTransformer,咱們能夠獲取已解析獲得的路有數據,而後選擇替換/增長某些路由數據,從而達到定製化spa
默認狀況下請求抵達時,若沒有找到匹配的終結點,就直接404了,咱們但願當沒有匹配到任何終結點時直接執行某個默認的終結點,能夠用以下方式:
endpoints.MapFallbackToController("{controller}/{action}/{id?}", "kkk", "jj");
當請求抵達時,若是沒有匹配到任何終結點,則默認執行jjController.kkk方法。能夠想象獲得此功能多是經過動態路由實現的
還有幾個相關的擴展方法,有了上面的講解,估計你也能猜出是幹嗎用的了。關於路由註冊就暫時說這麼多
app.UseRouting();對應概述中的步驟3,此擴展方法內部會註冊一箇中間件,未來請求抵達時它會幫咱們找到與當前請求匹配的終結點並存儲在HttpContext中,且匹配過程當中解析獲得的路由數據在Request.RouteValues中。咱們能夠在它後面加入本身的中間件
1 app.UseRouting(); 2 app.Use((conttext,next)=> { 3 var endpoint = conttext.GetEndpoint();//拿到終結點 4 var routeData = conttext.Request.RouteValues;//拿到路由數據
//作些牛B的事 5 return next(); 6 });