(8)ASP.NET Core 中的MVC路由(一)

1.前言

ASP.NET Core MVC使用路由中间件来匹配传入请求的URL并将它们映射到操做(Action方法)。路由在启动代码(Startup.Configure方法)或属性(Controller Action属性)中定义。路由描述应如何将URL路径与操做(Action方法)相匹配。它还用于在响应中生成送出的URL。
路由操做能够设置中间件,支持传统路由、属性理由(经过在Controller Action上放置理由可实现)、多个路由。api

2.设置路由中间件

在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

3.传统路由

默认路由:
routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}");
上述代码就是一个传统路由,将这种方式称为传统路由的缘由在于,它为URL路径设立了一个约定:
●第一个路径段映射到控制器名称
●第二段映射到操做名称。
●第三段用于可选id(用于映射到模型实体)。
使用此default路由时,URL路径/Home/Index映射到HomeController.Index。此映射仅基于控制器和操做(action)名称,而不基于命名空间、源文件位置或方法参数。框架

4.多个路由

4.1定义多个路由

经过对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

4.2区分操做

当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异常。

5.属性路由

5.1 属性路由

属性路由使用一组属性将操做(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页面。

5.2 使用 Http[Verb] 属性的属性路由

属性路由还可使用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约束来肯定能够执行的操做。

5.3合并路由

根据业务场景若是咱们须要将某个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去,减小路由属性重复。

5.4指定属性路由的可选参数、默认值和约束

属性路由同时也支持使用与传统路由相同的内联语法,来指定可选参数、默认值和约束。下面请看示例:

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参数值约束为只有数字才能打开该属性下视图,若是传入是字符串则没法找到该视图。而更多路由约束模板在这里参阅。

5.4自定义属性路由

咱们能够经过框架自带的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连接时访问成功,也就是说自定义属性路由成功。

 

参考文献:
在 ASP.NET Core 中路由到控制器操做

相关文章
相关标签/搜索