ASP.NET Core MVC使用路由中間件來匹配傳入請求的URL並將它們映射到操做(Action方法)。路由在啓動代碼(Startup.Configure方法)或屬性(Controller Action屬性)中定義。路由描述應如何將URL路徑與操做(Action方法)相匹配。它還用於在響應中生成送出的URL。
路由操做能夠設置中間件,支持傳統路由、屬性理由(經過在Controller Action上放置理由可實現)、多個路由。api
在Configure方法中,可能會看到與下面相似的設置路由中間件代碼:瀏覽器
app.UseMvc(routes => { routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });
等價於mvc
//默認模版就是 "{controller=Home}/{action=Index}/{id?}" app.UseMvcWithDefaultRoute();
在UseMvc的匿名方法中,MapRoute表示建立單個路由,指定爲默認(default)路由、路由模版「{controller=Home}/{action=Index}/{id?}」。
路由模版「{controller=Home}/{action=Index}/{id?}」的意思是能夠匹配「/Home/Index/5」的URL路徑。也就是說:
●{controller=Home}將Home定義爲默認controller。
●{action=Index}將Index定義爲默認action。
●{id?}將id定義爲可選參數。
UseMvc和UseMvcWithDefaultRoute可向中間件管道添加RouterMiddleware的實例。MVC不直接與中間件交互,而是使用路由來處理請求。MVC經過MvcRouteHandler實例鏈接到路由。 重載UseMvc(Action<IRouteBuilder>) 則容許用戶添加本身的路由,而且還支持屬性路由。UseMvcWithDefaultRoute定義默認路由並支持屬性路由。app
默認路由:
routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}");
上述代碼就是一個傳統路由,將這種方式稱爲傳統路由的緣由在於,它爲URL路徑設立了一個約定:
●第一個路徑段映射到控制器名稱
●第二段映射到操做名稱。
●第三段用於可選id(用於映射到模型實體)。
使用此default路由時,URL路徑/Home/Index映射到HomeController.Index。此映射僅基於控制器和操做(action)名稱,而不基於命名空間、源文件位置或方法參數。框架
經過對UseMvc匿名方法添加多個路由,這樣作能夠定義多個約定或添加專用於特定操做的傳統路由,好比:ide
app.UseMvc(routes => { //第一個路由 routes.MapRoute("blog", "blog/{*article}",defaults: new { controller = "Blog", action = "Article" }); //第二個路由 routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}"); });
第一個路由裏{*article}是表示使用catch-all路由參數來捕獲URL路徑的剩餘部分。下面咱們經過測試來驗證下:測試
●"blog/{*article}":ui
●"blog/{article}":spa
經過上述測試能夠知道,加星號的路由在傳參字符串中加了斜槓依然可以打開,沒加星號的路由在傳參字符串中加了斜槓就不能打開了。還有一點要注意的是controller.action接收參數必須跟*後面參數名稱一致,否則會接收不到參數值。3d
當controller存在兩個action名稱一致的方法,MVC必需要進行區分,否則會引起異常,例如:
public class BlogController : Controller { public IActionResult Edit(int id) {} [HttpPost] public IActionResult Edit(int id, Blog blog) {} }
當請求爲HTTP POST時MVC會選擇Edit(int, Blog),在Http屬性爲任何其餘內容時選擇 Edit(int)。若是匹配多個路由,而MVC找不到「最佳」路由Action時,則會引起 AmbiguousActionException異常。
屬性路由使用一組屬性將操做(Action)直接映射到路由模板。 在下面的示例中,Configure 方法使用app.UseMvc(),不傳遞任何路由。HomeController將匹配一組URL,這組URL與默認路由{controller=Home}/{action=Index}/{id?}匹配的URL相似:
public class HomeController : Controller { [Route("")] [Route("Home")] [Route("Home/Index")] public IActionResult Index() { return View(); } }
下面咱們經過表格來看看每一個屬性路由在瀏覽器上打開連接是怎樣的效果。
Route Template |
Browser |
Page |
Route("") |
[SERVICE_NAME] |
[SERVICE_NAME]/Home/Index |
Route("Home") |
[SERVICE_NAME]/Home |
|
Route("Home/Index") |
[SERVICE_NAME]/Home/Index |
三種屬性路由在瀏覽器上打開連接方式都不同,可是呈現頁面是同樣的,也就是說三個屬性路由的定義方式都是指向同一個/Home/Index頁面。到這裏或許大夥會有疑問,若是我把三個屬性路由模版名稱都改變下,那會能打開同一個頁面麼?
[Route("1")] [Route("Home1")] [Route("Home1/Index1")]
上述模版名稱更改了,可是在瀏覽器呈現頁面仍是指向/Home/Index的,也就是說,不管屬性路由模版名稱如何更改總能打開對應/Home/Index頁面。
屬性路由還可使用Http[Verb]屬性,好比HttpPostAttribute。全部這些屬性均可採用路由模板。此示例展現與同一路由模板匹配的兩項操做:
public class BlogController : Controller { [HttpGet("/article/{id}", Name = "Article_List")] public IActionResult Article(string id) { return View(); } }
當咱們在瀏覽器上輸入[SERVICE_NAME]/article/1時候,經過DEBUG咱們能夠看到響應結果:
經過響應結果能夠知道屬性路由首先將URL與路由屬性定義的路由模板集([HttpGet("/article/{id}", Name = "Article_List")])進行匹配。一旦某個路由模板匹配,就會應用IActionConstraint約束來肯定能夠執行的操做。
根據業務場景若是咱們須要將某個Controller從新命名爲以便訪問該Controller下面全部action,那麼該如何設置呢?下面經過示例來演示下:
[Route("homes")] public class HomeController : Controller { [HttpGet("{id}")] public IActionResult Index() { return View(); } }
根據上述代碼,在HomeController上放置路由屬性([Route("homes")])會使控制器中的全部操做(Action)都使用該屬性路由。也就是說只有URL HTTPGet :[SERVICE_NAME]/homes/1才能訪問Index視圖。這種作法好處是,HomeController下的每一個Action操做不用單獨添加Route("homes")屬性,只須要在HomeController外放置一個全局Route("homes")屬性便可應用到每一個Action去,減小路由屬性重複。
屬性路由同時也支持使用與傳統路由相同的內聯語法,來指定可選參數、默認值和約束。下面請看示例:
public class HomeController : Controller { [HttpGet("homes/{id:int}")] public IActionResult Index(int id) { return View(); } }
上述代碼執行結果咱們經過表格來看看效果:
Browser |
Result status |
[SERVICE_NAME]/homes/1 |
200 |
[SERVICE_NAME]/homes/qwe |
404 |
[HttpGet("homes/{id:int}")]把傳入id參數值約束爲只有數字才能打開該屬性下視圖,若是傳入是字符串則沒法找到該視圖。而更多路由約束模板在這裏參閱。
咱們能夠經過框架自帶的IRouteTemplateProvider接口自定義路由屬性(例如相似[HttpGet]、[Route("homes")]等屬性)。當應用程序啓動時,MVC會查找控制器類和操做方法上的屬性,並使用實現IRouteTemplateProvider的屬性生成一組初始路由。此部分經過一個簡單的示例說明了如何使用應用程序自定義屬性路由:
public class MyControllerAttribute : Attribute, IRouteTemplateProvider { //實現接口的三個屬性,這裏的[controller]是一個標記替換。 public string Template => "api/[controller]/{action}/{id?}"; public int? Order { get; set; } public string Name { get; set; } }
新建一個路由屬性類自定義屬性路徑,而後在Controller或者Action上放置自定義屬性路由。
public class BlogController : Controller { [MyController()] public IActionResult Article(string id) { return View(); } }
經過執行[SERVICE_NAME]/api/Blog/Article/1連接時訪問成功,也就是說自定義屬性路由成功。