使用UseRouting和UseEndpoints中间件配置路由。要使用控制器:
- 调用MapControllers来映射属性路由控制器。
- 调用MapControllerRoute或MapAreaControllerRoute来映射传统路由控制器和属性路由控制器。
- MapDefaultControllerRoute(); 正是上面约定路由的默认样例
以下示例中的字符串 "blog"和是常规路由名称:"default"
C#复制
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
路由名称给路由一个逻辑名称。命名路由可用于 URL 生成。当路由的顺序可能使 URL 生成变得复杂时,使用命名路由可以简化 URL 创建。路由名称在应用程序范围内必须是唯一的。
路线名称:
路由名称概念在路由中表示为IEndpointNameMetadata。术语路由名称和端点名称:
REST API 应该使用属性路由将应用程序的功能建模为一组资源,其中操作由HTTP 动词表示。
属性路由使用一组属性将操作直接映射到路由模板。以下代码是 REST API 的典型代码,将在下一个示例中使用:
app.MapControllers();
在前面的代码中,内部调用了MapControllersUseEndpoints来映射属性路由控制器。
在以下示例中:
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult Index(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult About(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
这个例子突出了属性路由和传统路由之间的关键编程区别。属性路由需要更多输入来指定路由。传统的默认路由处理路由更简洁。但是,属性路由允许并且需要精确控制哪些路由模板适用于每个操作。
使用属性路由,控制器和动作名称在动作匹配中不起任何作用,除非使用令牌替换。以下示例匹配与上一个示例相同的 URL:
public class MyDemoController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult MyIndex(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult MyAbout(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
以下代码对actionand使用令牌替换controller:
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("[controller]/[action]")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("[controller]/[action]")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
ASP.NET Core 具有以下 HTTP 动词模板:
ASP.NET Core 具有以下路由模板:
考虑以下控制器:
[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
[HttpGet] // GET /api/test2
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")] // GET /api/test2/xyz
public IActionResult GetProduct(string id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpGet("int/{id:int}")] // GET /api/test2/int/3
public IActionResult GetIntProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpGet("int2/{id}")] // GET /api/test2/int2/3
public IActionResult GetInt2Product(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
在前面的代码中:
C#复制
C#复制
C#复制
属性路由可以使用HttpMethodAttribute属性,例如HttpPostAttribute、HttpPutAttribute和HttpDeleteAttribute。所有HTTP 谓词属性都接受一个路由模板。以下示例显示了匹配同一路由模板的两个操作:
[HttpGet("int2/{id}")] // GET /api/test2/int2/3
public IActionResult GetInt2Product(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpGet("int/{id:int}")] // GET /api/test2/int/3
public IActionResult GetIntProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpGet("{id}")] // GET /api/test2/xyz
public IActionResult GetProduct(string id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
属性路由可以使用HttpMethodAttribute属性,例如HttpPostAttribute、HttpPutAttribute和HttpDeleteAttribute。所有HTTP 谓词属性都接受一个路由模板。以下示例显示了匹配同一路由模板的两个操作:
[ApiController]
public class MyProductsController : ControllerBase
{
[HttpGet("/products3")]
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpPost("/products3")]
public IActionResult CreateProduct(MyProduct myProduct)
{
return ControllerContext.MyDisplayRouteInfo(myProduct.Name);
}
}
使用 URL 路径/products3:
在构建 REST API 时,您很少需要[Route(...)]在操作方法上使用,因为该操作接受所有 HTTP 方法。最好使用更具体的HTTP 动词属性来准确了解您的 API 支持什么。REST API 的客户端应该知道哪些路径和 HTTP 动词映射到特定的逻辑操作。
REST API 应该使用属性路由将应用程序的功能建模为一组资源,其中操作由 HTTP 动词表示。这意味着许多操作(例如,对同一逻辑资源的 GET 和 POST)使用相同的 URL。属性路由提供了仔细设计 API 的公共端点布局所需的控制级别。
由于属性路由适用于特定操作,因此很容易将所需的参数作为路由模板定义的一部分。在以下示例中,id需要作为 URL 路径的一部分:
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
以下代码定义了一个路由名称Products_List:
C#复制
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
路由名称可用于根据特定路由生成 URL。路线名称:
路由名称在应用程序范围内必须是唯一的。
将上述代码与常规默认路由进行对比,默认路由将id参数定义为可选({id?})。精确指定 API 的能力具有优势,例如允许/products和/products/5被分派到不同的操作。
为了减少属性路由的重复性,控制器上的路由属性与各个操作的路由属性相结合。控制器上定义的任何路由模板都会添加到操作上的路由模板之前。在控制器上放置一个路由属性使得控制器中的所有动作都使用属性路由。
[ApiController]
[Route("products")]
public class ProductsApiController : ControllerBase
{
[HttpGet]
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
在前面的示例中:
这两个操作都只匹配 HTTP GET,因为它们都标有[HttpGet]属性。
应用于以/或~/不与应用于控制器的路由模板组合的操作的路由模板。
路由构建一棵树并同时匹配所有端点:
例如,属性路由 likeblog/search/{topic}比属性路由 like 更具体blog/{*article}。默认情况下blog/search/{topic},路由具有更高的优先级,因为它更具体。使用常规路由,开发人员负责按所需顺序放置路由。
属性路由可以使用Order属性配置订单。框架提供的所有路由属性包括Order. Order根据属性的升序处理路由。默认顺序是0. 使用Order = -1在不设置顺序的路线之前运行来设置路线。Order = 1在默认路线排序后使用运行设置路线。
避免依赖Order。如果应用程序的 URL 空间需要明确的顺序值才能正确路由,那么它也可能会让客户端感到困惑。一般来说,属性路由选择正确的路由与 URL 匹配。如果用于 URL 生成的默认顺序不起作用,则使用路由名称作为替代通常比应用Order属性更简单。
考虑以下两个控制器,它们都定义了路由匹配/home:
C#复制
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult Index(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult About(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
C#复制
public class MyDemoController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult MyIndex(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult MyAbout(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
使用上述代码进行请求/home会引发类似于以下内容的异常:
文本复制
AmbiguousMatchException: The request matched multiple endpoints. Matches:
WebMvcRouting.Controllers.HomeController.Index
WebMvcRouting.Controllers.MyDemoController.MyIndex
添加Order到其中一个路由属性可以解决歧义:
C#复制
[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
return ControllerContext.MyDisplayRouteInfo();
}
使用前面的代码,/home运行HomeController.Index端点。要到达MyDemoController.MyIndex,请求/home/MyIndex。注意:
有关使用 Razor Pages 的路线顺序的信息,请参阅Razor Pages 路线和应用程序约定:路线顺序。
在某些情况下,会返回带有不明确路由的 HTTP 500 错误。使用日志记录查看哪些端点导致了AmbiguousMatchException.
为方便起见,属性路由通过将标记括在方括号 ( , )中来支持标记替换。标记、和被替换为定义路由的操作中的操作名称、区域名称和控制器名称的值:[][action][area][controller]
C#复制
[Route("[controller]/[action]")]
public class Products0Controller : Controller
{
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
在前面的代码中:
C#复制
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
C#复制
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
令牌替换是构建属性路由的最后一步。上述示例的行为与以下代码相同:
令牌替换是构建属性路由的最后一步。上述示例的行为与以下代码相同:
C#复制
public class Products20Controller : Controller
{
[HttpGet("[controller]/[action]")] // Matches '/Products20/List'
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("[controller]/[action]/{id}")] // Matches '/Products20/Edit/{id}'
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
属性路由也可以与继承相结合。这与令牌替换相结合非常强大。令牌替换也适用于由属性路由定义的路由名称。 [Route("[controller]/[action]", Name="[controller]_[action]")]为每个动作生成一个唯一的路由名称:
C#复制
[ApiController]
[Route("api/[controller]/[action]", Name = "[controller]_[action]")]
public abstract class MyBase2Controller : ControllerBase
{
}
public class Products11Controller : MyBase2Controller
{
[HttpGet] // /api/products11/list
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")] // /api/products11/edit/3
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
要匹配文字标记替换分隔符[or ],请通过重复字符 ( [[or ]]) 对其进行转义。
属性路由支持定义到达同一动作的多个路由。最常见的用法是模仿默认常规路由的行为,如下例所示:
C#复制
[Route("[controller]")]
public class Products13Controller : Controller
{
[Route("")] // Matches 'Products13'
[Route("Index")] // Matches 'Products13/Index'
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
将多个路由属性放在控制器上意味着每个路由属性都与操作方法上的每个路由属性相结合:
C#复制
[Route("Store")]
[Route("[controller]")]
public class Products6Controller : Controller
{
[HttpPost("Buy")] // Matches 'Products6/Buy' and 'Store/Buy'
[HttpPost("Checkout")] // Matches 'Products6/Checkout' and 'Store/Checkout'
public IActionResult Buy()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
所有HTTP 谓词路由约束都实现IActionConstraint。
当多个实现IActionConstraint的路由属性被放置在一个动作上时:
C#复制
[Route("api/[controller]")]
public class Products7Controller : ControllerBase
{
[HttpPut("Buy")] // Matches PUT 'api/Products7/Buy'
[HttpPost("Checkout")] // Matches POST 'api/Products7/Checkout'
public IActionResult Buy()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
在操作上使用多个路由可能看起来有用且强大,最好保持应用程序的 URL 空间基本且定义明确。仅在需要时对操作使用多个路由,例如,支持现有客户端。
ASP.NET Core 应用程序可以混合使用传统路由和属性路由。为浏览器提供 HTML 页面的控制器使用常规路由,为提供 REST API 的控制器使用属性路由是典型的做法。
动作要么是传统路由,要么是属性路由。在控制器或操作上放置路由使其属性路由。定义属性路由的动作不能通过常规路由到达,反之亦然。控制器上的任何路由属性都会使控制器属性中的所有动作都被路由。
属性路由和常规路由使用相同的路由引擎。
总结:
动作要么是传统路由,要么是属性路由。在控制器或操作上放置路由使其属性路由。定义属性路由的动作不能通过常规路由到达,反之亦然。控制器上的任何路由属性都会使控制器属性中的所有动作都被路由。
属性路由和常规路由使用相同的路由引擎。
[HttpGet] 是谓词可以与传统路由结合,[HttpGet("")]是维持属性路由可与[Rout("")]结合
[ApiController]属性使得属性路由成为一个需求,既:必须同时使用[Route("")]。例如:
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
无法通过UseMvc或UseMvcWithDefaultRoute定义的常规路由访问操作。UseEndpoints
本文链接:https://blog.nnwk.net/article/61
有问题请留言。版权所有,转载请在显眼位置处保留文章出处,并留下原文连接
Leave your question and I'll get back to you as soon as I see it. All rights reserved. Please keep the source and links
友情链接:
子卿全栈
全部评论