路由匹配按注册顺序逐条尝试,首条完全匹配者执行;/products/abc因{id}无约束导致int id绑定失败而404,/products/123则成功;需用constraints(如new { id = "int" }或正则)限制格式,且属性路由须启用mapcontrollers()并确保中间件顺序正确。

路由匹配不是“查字典”,而是按注册顺序逐条尝试,第一条完全匹配的就执行,后续规则全部跳过。
为什么 /products/abc 返回 404,但 /products/123 能进控制器?
这是典型的路由约束缺失问题。没加约束时,{id} 会把任何字符串都塞进 Action 参数,但 int id 参数无法接收 "abc",导致模型绑定失败,MVC 默认静默返回 404(不抛异常)。
- 用正则约束:在
MapControllerRoute的constraints参数里写new { id = @"\d+" } - 用内建类型约束更简洁:
new { id = "int" }(等价于\d+,但不支持负数) - 若需支持负数或更复杂逻辑,必须用正则,比如
new { id = @"-?\d+" } - 注意:约束只影响“是否匹配该路由”,不负责参数转换;转换失败仍会 404
MapControllerRoute 第三个参数 defaults 和 constraints 混用会怎样?
defaults 是提供默认值,constraints 是限制输入格式,二者作用完全不同,但能共存。常见错误是把约束当默认值写,比如误写成 new { id = "123" } —— 这其实是给 id 设默认值为字符串 "123",不是约束。
- 正确写法示例:
new { controller = "Home", action = "Index" }(defaults) - 约束写法示例:
new { id = @"^[a-zA-Z0-9_]{3,20}$" }(正则约束用户名) - 混用示例:
endpoints.MapControllerRoute("user", "u/{username}", new { controller = "User", action = "Profile" }, new { username = @"^[a-zA-Z0-9_]{3,20}$" }) - defaults 中的键名必须和模板中占位符一致,否则无效;constraints 同理,且键名大小写敏感
为什么加了 [Route("api/products")] 还是 404?
属性路由(Attribute Routing)和约定路由(Convention Routing)是两套独立系统。如果只加了 [Route] 却没启用属性路由支持,框架根本不会扫描这些属性。
- .NET 6+ 必须在
Program.cs中调用builder.Services.AddControllers()或AddControllersWithViews() - 必须使用
app.MapControllers(),它才会识别[Route]、[HttpGet]等属性 - 不要混用:
MapControllerRoute和MapControllers()可共存,但同个请求只能被其中一种机制匹配 - 冲突时,属性路由优先级高于约定路由(只要启用了)
真正容易漏掉的是中间件顺序:UseRouting() 必须在 UseEndpoints() 或 MapControllers() 之前,且不能被其他中间件(比如自定义认证)意外中断匹配流程。









