前言
我是從.net 4.5直接跳到.net core 3.x的,感覺asp.net這套東西最初是從4.5中的owin形成的,
目前官方檔案重點是講路由,沒有特別說明與傳統路由的區別,本篇主要介紹終結點路由的相關概念和如何使用,不會詳細介紹路由,這個參考官方檔案就ok了,如果將來有機會研究到底層再深度剖析,
參考:
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/
概述
最初我們訪問 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等,將來還有更多,我們的路由系統能否提出來,讓所有框架都可以用?
因此出現了終結點路由,我們說路由的根本目的是將用戶請求地址,映射為一個請求處理器,最簡單的請求處理器可以是一個委托 Func<HttpCotnext,Task>,也可以是mvc/webapi中某個controller的某個action,所以從抽象的角度講 一個終結點 就是一個處理請求的委托,由于mvc中action上還有很多attribute,因此我們的終結點還應該提供一個集合,用來存盤與此請求處理委托的關聯資料,
從抽象的角度可以簡單理解為 一個終結點 = 處理請求的委托 + 與之關聯的附加(元)資料,對應到mvc來理解的話 終結點 = action + 應用其上的attribute集合,但記住終結點是個抽象的概念,并不只服務于mvc,原理大概如下:
- 在程式啟動前我們應該定義好程式中有哪些終結點,當然不是我們手動一個個定義,而是根據目標框架自動生成,針對mvc來說的話可以自動將程式中與路由匹配的action轉換成對應的終結點,其它框架應該也有對應的方式,反正最終我們所有用來處理請求的東東都變成了終結點,這步是在定義路由時自動完成的
- 除了定義終結點我們還要定義 請求路徑 與 終結點的對應關系,將來請求抵達時才能匹配找到合適的終結點來處理我們的請求,這步相當于定義路由
- 我們還需要定義一個決議器,當請求抵達時根據終結點與路徑的對應關系找到終結點,微軟已定義好對應的中間件來表示這個決議器,
- 最后我們需要定義一個中間件,在上面的中間件執行后 我們可以拿到與當前請求匹配的終結點,最終呼叫它的委托處理請求,這個中間件就是mvc中間件
- 到此asp.net core 3.x的中間件路由默認差不多就這樣了,此時我們可以定義自己的中間件,放在步驟3后面,拿到終結點做一些高級處理,微軟定義的一些中間件也是這個套路
如何使用
在通過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個任務
- 創建終結點定義,針對mvc來說會自動將程式中與路由格式匹配上的action轉換為終結點,在第5行之后可以除錯觀察endpoints.DataSource屬性,生成好的終結點就在里面
- 建立url與終結點的對應關系,這種關系存在哪?我也不曉得
- 注冊mvc中間件(它在將來請求抵達,且之前有中間件決議得到與當前請求匹配的終結點后,開始mvc旅程)
這里路由跟以前的寫法差不多,上面默認值啊、約束啊就去看官方檔案吧,
創建終結點也會參照屬性路由,微軟推薦webapi使用屬性路由,mvc使用傳統路由,你會看到創建默認webapi專案時這樣的 endpoints.MapControllers();
終結點進一步定制
默認情況下是根據定義的路由去找到匹配的action最后生成終結點,這個生成終結點的程序我們是可以參與的,具體辦法是通過endpoints.MapControllerRoute的回傳物件上呼叫相關擴展方法,本質上是向終結點的創建程序加入一些委托,將來創建終結點時,這些委托將被呼叫,代碼如下:
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容器中,最后呼叫一個擴展方法,看代碼:
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,我們可以獲取已決議得到的路有資料,然后選擇替換/增加某些路由資料,從而達到定制化
回退路由
默認情況下請求抵達時,若沒有找到匹配的終結點,就直接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 = https://www.cnblogs.com/jionsoft/p/conttext.Request.RouteValues;//拿到路由資料
//做些牛B的事5 return next();6 });
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/79171.html
標籤:.NET Core
