.NET Core MVC中HttpMethodAttribute路由匹配异常问题求助
Hey there, let's break down and solve this routing issue you're hitting in ASP.NET Core MVC 2.2.0.
The Root Cause
First, let's understand why your original setup behaved unexpectedly:
- When you used
[HttpPut("obj/{pk?}")], the routing system only checks if the URL structure matches the template—it doesn't validate the data type ofpkupfront. So when you send a request like/obj/AStringValue, the router still matches the route, but since "AStringValue" can't be converted to adecimal?, it just setspktonull, triggering your add logic instead of returning 404. - When you split into two methods, the problem with
pkbecoming0happens because value types likedecimalhave a default value of0. If the router matches the route with a non-decimalpkvalue, the binding system can't convert it, so it falls back to the default0instead of rejecting the request.
Fix #1: Use Route Constraints (Single Method Approach)
The cleanest fix is to add a decimal route constraint to your original method. This tells the router only to match the route if the pk segment is a valid decimal value. Non-decimal values will automatically return a 404 because the route won't be matched at all.
Here's the corrected code:
[HttpPut("obj/{pk:decimal?}")] public IActionResult SaveObj(decimal? pk, [FromBody] ObjDef obj) { if (!ModelState.IsValid) { return BadRequest(ModelState); // Optional: Catch any other binding issues } if (pk.HasValue) { // Execute update logic for the object with pk = pk.Value return Ok($"Updated object with PK {pk.Value}"); } else { // Execute add logic for a new object return CreatedAtAction(nameof(GetObj), new { pk = newCreatedPk }, obj); } }
Fix #2: Split Methods with Constraints (If You Prefer Separation)
If you want to keep separate methods for add and update, you still need to add the decimal constraint to the update method to prevent invalid values from triggering it. Here's how:
// Handles PUT /obj (add new object) [HttpPut("obj")] public IActionResult CreateObj([FromBody] ObjDef obj) { if (!ModelState.IsValid) { return BadRequest(ModelState); } // Add logic here return CreatedAtAction(nameof(GetObj), new { pk = newCreatedPk }, obj); } // Handles PUT /obj/123 (only valid decimal PKs) [HttpPut("obj/{pk:decimal}")] public IActionResult UpdateObj(decimal pk, [FromBody] ObjDef obj) { if (!ModelState.IsValid) { return BadRequest(ModelState); } // Update logic here return Ok($"Updated object with PK {pk}"); }
With this setup, requests like /obj/AStringValue won't match either route and will return a 404, just as you expected.
Key Takeaway
Route constraints are your friend here—they let you enforce data type validation at the routing layer, before the request even reaches your controller method. This avoids unexpected binding behavior and ensures only valid requests hit your business logic.
内容的提问来源于stack exchange,提问作者T.Z.

