3.5 路由 (Routing) 3.5 路由 (Routing) in ASP.NET Core MVC 路由是 ASP.NET Core MVC 应用的核心机制,它负责将传入的 HTTP 请求映射到应用程序中的特定控制器操作 (Action)。理解和掌握路由机制对于构建高效、可维护的 Web 应用至关重要。 3.5.1 路由的基本概念 路由本质上是一个模式匹配过程。当一个 HTTP 请求到达 ASP.NET Core 应用时,路由中间件会根据预定义的路由规则,尝试将请求的 URL 与这些规则进行匹配。如果找到匹配的规则,则会将请求转发到相应的控制器和操作。 关键概念包括: 路由规则 (Route Rule): 定义了 URL 模式和对应的控制器/操作之间的映射关系。
路由是 ASP.NET Core MVC 应用的核心机制,它负责将传入的 HTTP 请求映射到应用程序中的特定控制器操作 (Action)。理解和掌握路由机制对于构建高效、可维护的 Web 应用至关重要。
路由本质上是一个模式匹配过程。当一个 HTTP 请求到达 ASP.NET Core 应用时,路由中间件会根据预定义的路由规则,尝试将请求的 URL 与这些规则进行匹配。如果找到匹配的规则,则会将请求转发到相应的控制器和操作。
关键概念包括:
路由规则 (Route Rule): 定义了 URL 模式和对应的控制器/操作之间的映射关系。
路由模板 (Route Template): 定义了 URL 的结构,可以包含字面值和参数。
路由参数 (Route Parameter): URL 中可以动态变化的部分,用于传递数据给控制器操作。
路由约束 (Route Constraint): 限制路由参数的取值范围,例如只能是整数或特定格式的字符串。
路由处理程序 (Route Handler): 实际处理匹配到的路由的组件,通常是 MVC 控制器。
ASP.NET Core MVC 提供了多种配置路由的方式,最常见的是使用终结点路由(Endpoint Routing)。
终结点路由是 ASP.NET Core 3.0 引入的路由机制,它提供了更强大的功能和更好的性能。 在 Startup.cs 文件的 Configure 方法中,使用 UseEndpoints 方法来配置终结点路由。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); // 启用路由中间件 app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); // 可以添加更多的路由规则 }); }
代码解释:
app.UseRouting(): 将路由中间件添加到请求管道中。
app.UseEndpoints(): 配置终结点路由。
endpoints.MapControllerRoute(): 映射一个标准的 MVC 控制器路由。
name: 路由名称,用于在代码中引用该路由。
pattern: 路由模板,定义了 URL 的结构。
{controller=Home}: 控制器参数,默认值为 "Home"。
{action=Index}: 操作参数,默认值为 "Index"。
{id?}: 可选的 ID 参数。 ? 表示该参数是可选的。
示例:
/ -> HomeController.Index()
/Home -> HomeController.Index()
/Home/Index -> HomeController.Index()
/Home/About -> HomeController.About()
/Products/Details/123 -> ProductsController.Details(id = 123)
特性路由允许直接在控制器和操作上定义路由规则,使路由配置更加直观和集中。 使用 [Route] 特性来指定路由模板。
using Microsoft.AspNetCore.Mvc; namespace MyWebApp.Controllers { [Route("products")] // 控制器级别的路由 public class ProductsController : Controller { [Route("")] // 匹配 /products [Route("index")] // 匹配 /products/index public IActionResult Index() { return View(); } [Route("details/{id:int}")] // 匹配 /products/details/{id},id 必须是整数 public IActionResult Details(int id) { ViewBag.Id = id; return View(); } [Route("create")] //匹配 /products/create public IActionResult Create() { return View(); } } }
代码解释:
[Route("products")]: 定义了控制器级别的路由前缀。所有该控制器下的操作都将以 /products 开头。
[Route("")]: 表示匹配控制器路由前缀本身,即 /products。
[Route("index")]: 表示匹配 /products/index。
[Route("details/{id:int}")]: 定义了一个带参数的路由,{id:int} 表示 id 参数必须是整数类型。这就是一个路由约束。
可以同时使用终结点路由和特性路由,但这需要谨慎。 终结点路由通常用于定义默认路由和回退路由,而特性路由用于定义更具体的路由规则。
如果同时使用,特性路由会覆盖终结点路由。因此,建议尽量使用一种路由方式,以避免混淆。
路由参数是从 URL 中提取数据并传递给控制器操作的关键机制。路由约束用于限制路由参数的取值范围,以确保数据的有效性。
路由参数使用花括号 {} 括起来,可以定义参数名称和可选的默认值。
[Route("articles/{year}/{month}/{day}")] public IActionResult Articles(int year, int month, int day) { // ... }
在这个例子中,year、month 和 day 是路由参数。 当 URL 匹配到该路由时,这些参数的值将被提取并传递给 Articles 操作。
路由约束使用冒号 : 后跟约束名称来定义。ASP.NET Core 提供了许多内置的路由约束,例如:
int: 限制参数为整数。
bool: 限制参数为布尔值。
datetime: 限制参数为日期时间。
minlength(n): 限制参数的最小长度为 n。
maxlength(n): 限制参数的最大长度为 n。
range(min,max): 限制参数的取值范围在min和max之间。
regex(pattern): 使用正则表达式匹配参数。
[Route("products/{id:int:min(1)}")] // id 必须是大于等于 1 的整数 public IActionResult Details(int id) { // ... } [Route("users/{username:regex(^[a-zA-Z]+$)}")] // username 只能包含字母 public IActionResult Profile(string username) { // ... }
可以创建自定义的路由约束,以满足特定的需求。 需要实现 IRouteConstraint 接口。
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using System.Text.RegularExpressions; public class MobileRouteConstraint : IRouteConstraint { public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) { if (!values.ContainsKey(routeKey)) { return false; } var mobile = values[routeKey]?.ToString(); if (string.IsNullOrEmpty(mobile)) { return false; } // 简单的手机号验证 return Regex.IsMatch(mobile, @"^1[3456789]\d{9}$"); } }
然后,需要在 Startup.cs 中注册自定义路由约束:
public void ConfigureServices(IServiceCollection services) { services.Configure<RouteOptions>(options => { options.ConstraintMap.Add("mobile", typeof(MobileRouteConstraint)); }); services.AddControllersWithViews(); }
最后,可以在路由中使用自定义约束:
[Route("sms/{mobile:mobile}")] public IActionResult SendSms(string mobile) { // ... }
当多个路由规则匹配同一个 URL 时,路由引擎会根据优先级来选择最佳匹配。 路由优先级的规则如下:
更具体的路由优先: 包含更多字面值的路由优先。
包含约束的路由优先: 包含约束的路由优先。
显式定义的路由优先: 使用 MapControllerRoute 或特性路由定义的路由优先于默认路由。
注册顺序: 如果以上规则无法区分,则按照路由注册的顺序选择,先注册的路由优先。
除了将 URL 映射到控制器操作之外,路由还可以用于生成 URL。 可以使用 IUrlHelper 接口来生成 URL。
public class HomeController : Controller { private readonly IUrlHelper _urlHelper; public HomeController(IUrlHelper urlHelper) { _urlHelper = urlHelper; } public IActionResult Index() { var url = _urlHelper.Action("Details", "Products", new { id = 123 }); // 生成 /products/details/123 ViewBag.ProductDetailsUrl = url; return View(); } }
代码解释:
_urlHelper.Action(): 根据指定的控制器、操作和路由参数生成 URL。也可以使用 asp-route-{parameter} 标签助手在 Razor 视图中生成 URL:
<a asp-controller="Products" asp-action="Details" asp-route-id="456">Product Details</a>
这将生成一个指向 /products/details/456 的链接。
ASP.NET Core 提供了一些工具来帮助调试和诊断路由问题:
路由调试中间件: 可以显示每个请求匹配到的路由规则。 需要安装 Microsoft.AspNetCore.Diagnostics 包,并在 Startup.cs 中配置中间件。
日志记录: 路由引擎会记录路由匹配的信息,可以查看日志来了解路由匹配的过程。
路由是 ASP.NET Core MVC 应用的关键组成部分,它负责将 URL 映射到控制器操作,并生成 URL。 通过理解和掌握路由的配置方式、路由参数和约束、路由优先级以及 URL 生成,可以构建灵活、可维护的 Web 应用。 特性路由和终结点路由是两种主要的路由配置方式,可以根据实际需求选择使用。 路由约束可以用于限制路由参数的取值范围,确保数据的有效性。 IUrlHelper 和标签助手可以用于生成 URL。 路由调试中间件和日志记录可以帮助调试和诊断路由问题。