ASP.NET Core 3.1多约定路由优先级不符合预期问题咨询
一、为何请求/customer/sean/details会跳转到Details动作?
你的描述看起来有点矛盾——按你给出的路由配置,default路由先定义,理论上会把/customer/sean/details解析为controller=Customer、action=sean、id=details,而你的Customer控制器并没有sean动作,应该返回404才对。但实际却成功跳转到了Details动作,大概率是以下几种原因之一:
1. 属性路由优先级覆盖了约定路由
虽然你贴出的Customer控制器代码里没有属性路由,但如果实际项目中Details方法添加了类似这样的属性:
[HttpGet("customer/{name}/details")] public IActionResult Details(string name) { ViewBag.CustomerName = name; return View(); }
ASP.NET Core中属性路由的优先级高于约定路由,请求会直接匹配这个属性路由,完全跳过你定义的约定路由逻辑,所以会正确执行Details动作。
2. 路由顺序或模板被误配置
如果你的实际代码中:
CustomerHomepage路由被放在了default路由前面,且该路由的模板被误写为"customer/{name}/{action}"(而非你贴出的"customer/{name}/index"),那么/customer/sean/details会匹配这个路由,解析为controller=Customer、action=Details、name=sean,自然会执行对应的动作。- 或者你在其他地方修改了路由顺序,导致
default路由没有优先匹配。
3. 请求URL的拼写误差
有可能你实际发起的请求是/customer/details/sean而非/customer/sean/details,这时候default路由会解析为controller=Customer、action=Details、id=sean。如果你的Details方法参数name通过模型绑定(比如添加了[FromRoute(Name = "id")])接收id的值,也会成功执行并传递参数。
二、约定路由顺序依赖的最佳示例
约定路由的核心原则是:更具体、更特殊的路由放在前面,通用路由放在最后。因为ASP.NET Core会按路由添加的顺序依次匹配,先匹配到的路由会被优先使用,后面的路由不会再被检查。
最佳实践代码示例
app.UseEndpoints(endpoints => { // 1. 最具体的固定路由:仅匹配 /admin/dashboard 这个固定URL endpoints.MapControllerRoute( name: "AdminDashboard", pattern: "admin/dashboard", defaults: new { controller = "Admin", action = "Dashboard" }); // 2. 带参数的特定业务路由:匹配 /customer/{name}/orders 这类URL endpoints.MapControllerRoute( name: "CustomerOrders", pattern: "customer/{name}/orders", defaults: new { controller = "Customer", action = "Orders" }); // 3. 带默认值的通用路由:放在最后,处理所有常规的控制器/动作请求 endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); });
为什么要这样排序?
- 如果把通用的
default路由放在前面,像/admin/dashboard这样的固定URL会被错误解析为controller=Admin、action=dashboard,如果Admin控制器没有dashboard动作,就会返回404,而不会匹配到后面的AdminDashboard路由。 - 先定义特殊路由,可以确保特定业务URL被精准匹配,避免通用路由“拦截”正常的业务请求。
针对你的场景的修正建议
如果你希望/customer/{name}/index匹配Index动作,/customer/details/{name}匹配Details动作,应该把这些特定路由放在通用路由之前:
app.UseEndpoints(endpoints => { // 匹配 /customer/{name}/index endpoints.MapControllerRoute( name: "CustomerHomepage", pattern: "customer/{name}/index", defaults: new { controller = "Customer", action = "Index" }); // 匹配 /customer/details/{name} endpoints.MapControllerRoute( name: "CustomerDetails", pattern: "customer/details/{name}", defaults: new { controller = "Customer", action = "Details" }); // 通用默认路由放在最后 endpoints.MapControllerRoute( name: "default", pattern: "{controller}/{action}/{id?}"); });
这样配置后,你的业务请求都会被精准匹配,不会再出现路由解析异常的问题。
内容的提问来源于stack exchange,提问作者Sean Ch

