ASP.NET Core 8 MVC:Action无法使用id以外的命名路由值
你遇到的问题核心是ASP.NET Core中属性路由的匹配规则和ASP.NET MVC 5不一样,具体逻辑如下:
当你给Action添加[Route]属性后,这个Action会完全使用属性路由匹配请求,不再参与Startup里配置的约定路由(areas/default这类)。而你写的[Route("DoSomeDeletion/{uniqueIdentifier}")]是一个根相对路径模板(因为控制器没有加[Route]前缀),它对应的请求URL是/DoSomeDeletion/356,但你实际请求的是/SomeArea/SomeName/DoSomeDeletion/356,路径完全不匹配,所以永远命中不了断点。
当你移除[Route]属性后,Action会回到约定路由的匹配逻辑,你的areas路由模板{area:exists}/{controller=index}/{action=Index}/{id?}会匹配请求:area=SomeArea、controller=SomeName、action=DoSomeDeletion、id=356,所以能命中Action(不过此时你的参数uniqueIdentifier其实绑定不到值,因为路由参数名是id,和方法参数名不一致,你可以改成int id或者加[FromRoute(Name = "id")]来绑定)。
解决方法
给你几个可行的方案,按需选择:
方案1:给控制器加路由前缀,Action用相对模板
给控制器添加[Route]属性指定Area和Controller的路由前缀,Action的路由模板会基于这个前缀生成:
[Area("SomeArea")] [Route("[area]/[controller]")] // 用路由令牌自动匹配Area和Controller名称 public class SomeNameController : Controller { // ... [HttpDelete("DoSomeDeletion/{uniqueIdentifier}")] public async Task<JsonResult> DoSomeDeletion(int uniqueIdentifier) { // ... return Json(result); } }
这样Action的完整路由就是/SomeArea/SomeName/DoSomeDeletion/{uniqueIdentifier},和你的请求URL完全匹配,能正常命中。
方案2:Action用路由令牌写完整模板
不需要给控制器加[Route],直接在[HttpDelete]重载里用路由令牌指定完整路径:
[HttpDelete("[area]/[controller]/DoSomeDeletion/{uniqueIdentifier}")] public async Task<JsonResult> DoSomeDeletion(int uniqueIdentifier) { // ... }
这个方案和方案1一样灵活,不需要硬编码Area和Controller名称,路由令牌会自动匹配当前控制器的Area和名称。
方案3:硬编码完整路由模板(不推荐)
直接在Action的[Route]里写包含Area和Controller的完整路径:
[HttpDelete] [Route("SomeArea/SomeName/DoSomeDeletion/{uniqueIdentifier}")] public async Task<JsonResult> DoSomeDeletion(int uniqueIdentifier) { // ... }
缺点是硬编码了Area和Controller名称,后续改名需要同步修改,灵活性差。
为什么ASP.NET MVC 5里没这个问题?
ASP.NET MVC 5的属性路由实现和ASP.NET Core有差异:在MVC5中,即使控制器没有加[Route]前缀,Action上的相对[Route]模板会自动继承控制器的约定路由路径(比如/Area/Controller/),所以你之前的写法在MVC5里能正常匹配。但ASP.NET Core改变了这个行为,当控制器没有[Route]属性时,Action上的[Route]模板是从应用根路径开始的绝对路径,这就是两者的核心区别。
内容的提问来源于stack exchange,提问作者Peter

