一. 概述 介紹asp.net core路由時,我初步想了下,分幾篇來說明。 路由的知識點很多,參考了官方文檔提取出一些重要的知識點來說。 在ASP.NET Core中是使用路由中間件來匹配傳入請求的 URL 並將它們映射到操作(action方法)。路由是在程式啟動時進行傳統路由或屬性路由定義。 路 ...
一. 概述
介紹asp.net core路由時,我初步想了下,分幾篇來說明。 路由的知識點很多,參考了官方文檔提取出一些重要的知識點來說。 在ASP.NET Core中是使用路由中間件來匹配傳入請求的 URL 並將它們映射到操作(action方法)。路由是在程式啟動時進行傳統路由或屬性路由定義。 路由描述如何將 URL 路徑與操作相匹配。 它還用於在響應中生成送出的 URL(用於鏈接)。
路由操作既支持傳統路由,也支持屬性路由。也可混合使用。通常傳統路由用於為瀏覽器處理 HTML 頁面的控制器。屬性路由用於處理 web API 的控制器。
1.1設置路由中間件
要使用傳統路由,必須在UseMVC中間件中配置實現IRouteBuilder介面,在asp.net core mvc 2.2 框架下,應用程式Startup的Configure 方法中,預設路由設置如下:
app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });
在對 UseMvc調用中,MapRoute 用於創建單個路由,亦稱 default 路由。 大多數 MVC 應用使用帶有模板的路由。對於default路由簡便的方法可以使用:
app.UseMvcWithDefaultRoute();
UseMvc 和 UseMvcWithDefaultRoute 可向中間件管道添加 RouterMiddleware 的實例。 MVC 不直接與中間件交互,而是使用路由來處理請求。 MVC 通過 MvcRouteHandler 實例連接到路由。
UseMvc 不直接定義任何路由,它向屬性路由的路由集合添加占位符{controller=Home}/{action=Index}/{id?} 。通過重載 UseMvc(Action<IRouteBuilder>) 則允許用戶添加自己的路由,並且還支持屬性路由。
1.2 傳統路由
傳統路由是:具有描述性的路由方案,這樣URL具有可讀性。傳統路由格式:{controller=Home}/{action=Index}/{id?}這樣的url路徑是設定了一個約定: 第一段映射到控制器名稱, 第二段映射到操作名稱,第二段映射到可選ID。
(1) 使用預設路由:
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
使用此預設路由時: url路徑/Products/List 將映射到程式ProductsController(控制器).List(action)中。 url路徑/Blog/Article/17將映射到程式BlogController(控制器).Article(action)中。
(2) 多個路由:
通過添加對 MapRoute 的多次調用,可以在 UseMvc 內添加多個路由。 這樣做可以定義多個約定,或添加專用於特定操作的傳統路由,比如:
app.UseMvc(routes => { routes.MapRoute("blog", "blog/{*article}", defaults: new { controller = "Blog", action = "Article" }); routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}"); });
這裡的blog路由是一個專用的傳統路由,這表示blog使用傳統路由系統,但專用於特定的操作,也就是對於BlogController控制器的Article操作,此專用路由將始終映射。對於多個路由的路由集合會進行排序,並按添加順序進行處理,因此,在此示例中,將先嘗試 blog 路由,再嘗試 default 路由。
(3) action操作的區分
在處理url請求時,當通過路由匹配到一個控制器內兩項相同的action名稱時,mvc必須進行區分,以選擇最佳候選項,否則會引發異常(AmbiguousActionException)。
public class ProductsController : Controller { public IActionResult Edit(int id) { ... } [HttpPost] public IActionResult Edit(int id, Product product) { ... } }
此Products控制器定義了二項操作,這兩項操作均與 URL 路徑的 /Products/Edit/17 匹配相同。解決方案是將要提交的action加上 Http 謂詞為 POST。這樣post過來時,就會選擇Edit(int, Product)
1.3 屬性路由
通過在控制器(Controller)或操作(Action)上放置路由可實現屬性路由。 不能通過傳統路由訪問定義屬性路由的操作,反之亦然。 控制器上的任何路由屬性,都會使控制器中的所有操作使用屬性路由。
屬性路由使用一組屬性將action直接映射到路由模板。在下麵的示例中,Configure 方法使用 app.UseMvc();,不傳遞任何路由。 HomeController 將匹配一組 URL,這組 URL 與預設路由 {controller=Home}/{action=Index}/{id?} 匹配的 URL 類似:
當去掉default預設路由模板後,只使用app.UseMvc()時。運行程式時,頁面報404錯誤:找不到 localhost 的網頁。
app.UseMvc();
(1) 屬性路由基本使用
如果定義了屬性路由的操作,此時就是啟動屬性路由功能。Home控制器的屬性路由示例如下:
public class HomeController : Controller { [Route("")] [Route("Home")] [Route("Home/Index")] public IActionResult Index() { return View(); } }
在index的action上加[Route("")]屬性路由。 瀏覽器可以使用下麵三種url來訪問,也是程式啟動時的預設載入頁面:
http://localhost:30081/
http://localhost:30081/Home/
http://localhost:30081/Home/index
(2) 屬性路由精確控制
屬性路由需要更多輸入來指定路由;傳統的預設路由處理路由的方式則更簡潔。 但是,屬性路由允許(並需要)精確控制應用於每項操作的路由模板。下麵示例是精確控制每項操作的路由模板,比如url訪問/home/index時,即是調用MyIndex的action方法。
public class MyDemoController : Controller { [Route("")] [Route("Home")] [Route("Home/Index")] public IActionResult MyIndex() { return View("Index"); } }
1.4 使用 Http[Verb] 屬性的屬性路由
屬性路由還可以使用 Http[Verb]
屬性,比如 HttpPostAttribute
。 所有這些屬性都可採用路由模板。 此示例展示,同一路由模板匹配的兩項操作:
[HttpGet("/products")] public IActionResult ListProducts() { // ... } [HttpPost("/products")] public IActionResult CreateProduct(...) { // ... }
當 Http 謂詞為 GET 時將執行ProductsApi.ListProducts 操作, 當 Http 謂詞為 POST 時將執行 ProductsApi.CreateProduct。生成 REST API 時,很少會在操作方法上使用 [Route(...)]。 建議使用更特定的 Http*Verb*Attributes 來明確 API 所支持的操作。 REST API 的客戶端需要知道映射到特定邏輯操作的路徑和 Http 謂詞。
例如下麵一個web api訪問路由,使用Http*Verb*Attributes 來明確定義如下:
public class ProductsApiController : Controller { [HttpGet("/products/{id}", Name = "Products_List")] public IActionResult GetProduct(int id) { ... } }
上面定義只有針對如訪問url如: /products/3(而非 /products)之類的 URL才會執行 ProductsApi.GetProduct(int) 操作。
1.5 路由合併
若要使屬性路由減少重覆,可將控制器Controller上的路由屬性與各個操作Action上的路由屬性合併。 控制器上定義的所有路由模板均作為操作上路由模板的首碼。 在控制器上放置路由屬性會使控制器中的所有操作都使用屬性路由。
下麵是一個web api的路由合併,訪問Get的方法的訪問路徑為: http://localhost:30081/api/Products/1
[Route("api/Products")] public class ProductsApiController : Controller { // GET api/values/5 [HttpGet("{id}")] public string Get(int id) { return "value"; } }
下麵是一個控制器的路由合併。訪問index頁面的訪問路徑為: http://localhost:30081/home/index
[Route("Home")] public class HomeController : Controller { [Route("")] // Combines to define the route template "Home" [Route("Index")] // Combines to define the route template "Home/Index" [Route("/")] // Doesn't combine, defines the route template "" public IActionResult Index() { //... } }
1.6 指定屬性路由參數約束
[HttpGet("Home/{id:int}",Name = "Pri")] public IActionResult Privacy(int id) { return View(); }
如果輸入非整數類型的參數,瀏覽器提示:找不到與以下網址對應的網頁:http://localhost:30081/home/dd
1.7 自定義路由屬性
該框架中提供的所有路由屬性([Route(...)]、[HttpGet(...)] 等)都可實現 IRouteTemplateProvider介面。 當應用啟動時,MVC 會查找控制器類和操作方法上的屬性,並使用可實現 IRouteTemplateProvider的屬性生成一組初始路由。
下麵使用IRouteTemplateProvider
來定義自己的路由屬性。每個 IRouteTemplateProvider
都允許定義一個包含自定義路由模板、順序和名稱的路由:
public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider { //實現介面的三個屬性,這裡的[controller]是一個標記替換。 public string Template => "api/[controller]/{action}/{id?}"; public int? Order { get; set; } public string Name { get; set; } } public class ProductsApiController : Controller { // GET api/values/5 // [HttpGet("{id}")] [MyApiController()] public string Get(int id) { return "value"; } }
通過訪問url: http://localhost:30081/api/ProductsApi/get/1 來調用get方法。
參考文獻
官方資料:asp.net core routing