介紹asp.net core路由時,我初步想了下,分幾篇來講明。 路由的知識點不少,參考了官方文檔提取出一些重要的知識點來講。 在ASP.NET Core中是使用路由中間件來匹配傳入請求的 URL 並將它們映射到操做(action方法)。路由是在程序啓動時進行傳統路由或屬性路由定義。 路由描述如何將 URL 路徑與操做相匹配。 它還用於在響應中生成送出的 URL(用於連接)。web
路由操做既支持傳統路由,也支持屬性路由。也可混合使用。一般傳統路由用於爲瀏覽器處理 HTML 頁面的控制器。屬性路由用於處理 web API 的控制器。
api
要使用傳統路由,必須在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路由簡便的方法能夠使用:mvc
app.UseMvcWithDefaultRoute();
UseMvc 和 UseMvcWithDefaultRoute 可向中間件管道添加 RouterMiddleware 的實例。 MVC 不直接與中間件交互,而是使用路由來處理請求。 MVC 經過 MvcRouteHandler 實例鏈接到路由。 UseMvc 不直接定義任何路由,它向屬性路由的路由集合添加佔位符{controller=Home}/{action=Index}/{id?} 。經過重載 UseMvc(Action<IRouteBuilder>) 則容許用戶添加本身的路由,而且還支持屬性路由。
傳統路由是:具備描述性的路由方案,這樣URL具備可讀性。傳統路由格式:{controller=Home}/{action=Index}/{id?}這樣的url路徑是設定了一個約定: 第一段映射到控制器名稱, 第二段映射到操做名稱,第二段映射到可選ID。app
(1) 使用默認路由: 框架
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
使用此默認路由時: url路徑/Products/List 將映射到程序ProductsController(控制器).List(action)中。 url路徑/Blog/Article/17將映射到程序BlogController(控制器).Article(action)中。asp.net
(2) 多個路由:ide
經過添加對 MapRoute 的屢次調用,能夠在 UseMvc 內添加多個路由。 這樣作能夠定義多個約定,或添加專用於特定操做的傳統路由,好比:post
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 路由。ui
(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)
經過在控制器(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"); } }
屬性路由還能夠使用 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) 操做。
若要使屬性路由減小重複,可將控制器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() { //... } }
[HttpGet("Home/{id:int}",Name = "Pri")] public IActionResult Privacy(int id) { return View(); }
若是輸入非整數類型的參數,瀏覽器提示:找不到與如下網址對應的網頁:http://localhost:30081/home/dd
該框架中提供的全部路由屬性([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