public class BlogController : Controller { public ActionResult Edit(int id) { return View(); } [HttpPost] public ActionResult Edit(int id, IFormCollection collection) { try { // TODO: Add update logic here return RedirectToAction(nameof(Index)); } catch { return View(); } } }
URL /Blog/Edit/7 可以匹配这两个操作,这是MVC控制器的典型模式,其中Edit(int)用于显示编辑的表单,Edit(int,IFormCollection)用于 处理已提交的表单。为了达到这个目的,MVC需要在HTTP POST时选择Edit(int,IFormCollection),在其他HTTP动词时选择Edit(int)。
HttpPostAttribute 是IActionConstraint 的一个实现,它只允许在HTTP动词为POST时选择动作。IActionConstraint的存在使得Edit(int,IFormCollection)比Edit(int)更好匹配。
如果有多个路由匹配,并且MVC无法找到一个最佳路由,则会抛出AmbiguousActionException异常。
3.路由名称
上面的例子中"blog"和"default"字符串是路由名称,路由名称为路由提供了一个逻辑名称,以便命名的路由可用于生成URL。在应用程序范围内路由必须名称必须是唯一的。
路由名称对URL匹配或请求的处理没有影响,仅用于URL生成。
4.路由特性
特性路由使用一组特性直接将操作映射到路由模板。下面在Configure中调用 app.UseMvc(); 没有传递路由。
public class HomeController : Controller { [Route("")] [Route("Home")] [Route("Home/Index")] public IActionResult Index() { return View(); } [Route("Home/About")] public IActionResult About() { ViewData["Message"] = "Your application description page."; return View(); } }
HomeController.Index操作将会对 /,/Home 或者/Home/Index 任一URL访问执行。
特性路由需要有更多的输入来指定一个路由,而常规路由处理路由时更加简洁。然而,特性路由允许精准控制每个操作的路由模板。
上面的模板中没有定义针对 action,area ,controller的路由参数。实际上,这些参数不允许出现在特性路由中,因为路由模板已经关联了一个操作,解析URL中的操作名是没有意义的。
特性路由也可以使用HTTP[Verb]特性,如HTTPPost:
[HttpGet("/Blog")] public ActionResult Index() { return View(); }
由于特性路由适用于特定操作,因此很容易使参数作为模板定义中必须的一部分。下面的例子,id是URL中必须的一部分:
[HttpGet("Blog/Edit/{id}")] public ActionResult Edit(int id) { return View(); }
常规的默认路由定义id参数作为可选项({id?}),而特性路由的是必须参数,这种可以精准指定,比如包/Blog/Get 和 /Blog/Get/{id} 分配到不同的操作。
5.组合路由
为了减少特性路由的重复部分,控制器上的路由特性会和各个操作上的路由特性进行结合。任何定义在控制器上的路由模板都会作为操作路由模板的前缀。
[Route("blog")] public class BlogController : Controller { [HttpGet] public ActionResult GetAll() { return View(); } [HttpGet("{id}")] public ActionResult GetById(int id) { return View(); } }
/blog 匹配GetAll方法, /blog/1 匹配 GetById方法。
注意,如果操作上路由模板以 / 开头时不会结合控制器上的路由模板。
6.特性路由的顺序
常规路由会根据定义顺序来执行,与之相比,特性路由会构建一个树形结构,同时匹配所有路由。这种看起来像路由条目被放置在一个理想的顺序中,最具体的路由会在一般的路由之前执行。比如,路由blog/Edit/4 比 blog/{*article} 更加具体。
特性路由使用所有框架提供的路由特有的Order属性来配置顺序,并根据Order属性升序处理路由。默认是0,设置为-1时会在没有设置的路由之前执行。
7.路由模板中的标记替换( [controller] , [action] , [area])
为了方便,特性路由支持标记替换,即通过在在方括号中封闭一个标记([, ])来替换对应的名称。标记[action],[area],[controller]会被替换成操作所对应的操作名,区域名,控制器名。
[Route("[controller]/[action]")] public class BlogController : Controller { [HttpGet]//匹配Blog/GetAll public ActionResult GetAll() { return View(); } }
标记替换发生在构建特性路由的最后一步。与上面结果相同的写法:
public class BlogController : Controller { [HttpGet("[controller]/[action]")]//匹配Blog/GetAll public ActionResult GetAll() { return View(); } }
特性路由也可以与继承相结合,即继承父类的路由标记。
特性路由支持单个操作定义路由。如果用IActionConstarint实现的多个路由特性定义在一个操作上时,每个操作约束与特性定义的路由相结合: