) 则允许用户添加自己的路由,并且还支持属性路由。
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