在 ASP.NET Core 中,模型绑定(Model Binding)是将 HTTP 请求中的数据(如路由参数、查询字符串、请求体等)自动映射到控制器 Action 方法参数的过程。
当多个数据源中存在同名参数时,框架会按照一个固定的优先级顺序来决定使用哪个值。
📋 默认绑定源优先级
ASP.NET Core 的模型绑定器会按照以下顺序依次查找数据,并使用第一个找到的值。
- 表单值 (Form values)
- 来自
application/x-www-form-urlencoded或multipart/form-data请求的表单字段。
- 来自
- 路由值 (Route values)
- 来自 URL 路由模板中定义的参数(例如
/api/products/{id}中的id)。
- 来自 URL 路由模板中定义的参数(例如
- 查询字符串 (Query string)
- 来自 URL 中
?后面的键值对(例如/api/products?id=5)。
- 来自 URL 中
- 请求体 (Request body)
- 来自
application/json、application/xml等格式的请求体内容。 - 特别注意:对于复杂类型(如自定义的类对象),默认情况下会从请求体中绑定。
- 来自
核心规则:一旦在某个源中找到了匹配的参数名,绑定过程就会停止,后续优先级较低的源中即使有同名参数也会被忽略。
💻 代码示例
假设有以下控制器和 Action:
csharp
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
// GET /api/products/5?price=100
[HttpGet("{id}")]
public IActionResult GetProduct(int id, int price)
{
// 此时 id = 5 (来自路由), price = 100 (来自查询字符串)
return Ok(new { Id = id, Price = price });
}
}
现在,我们通过一个 POST 请求来演示优先级冲突:
请求:
http
POST /api/products/99?price=50
Content-Type: application/x-www-form-urlencoded
price=200&id=88
模型绑定结果:
id参数:- 首先在表单值 中查找,找到
id=88。 - 绑定成功,
id的值为88。路由中的99被忽略。
- 首先在表单值 中查找,找到
price参数:- 首先在表单值 中查找,找到
price=200。 - 绑定成功,
price的值为200。查询字符串中的50被忽略。
- 首先在表单值 中查找,找到
最终,Action 方法接收到的参数是 id=88 和 price=200。
🛠️ 使用特性控制绑定源
你可以使用 [From...] 系列特性来显式指定参数的绑定源,这会覆盖默认的优先级规则。
| 特性 | 说明 |
|---|---|
[FromForm] |
强制从表单值中绑定。 |
[FromRoute] |
强制从路由值中绑定。 |
[FromQuery] |
强制从查询字符串中绑定。 |
[FromBody] |
强制从请求体中绑定。 |
[FromHeader] |
从请求头中绑定。 |
[FromServices] |
从依赖注入容器中解析服务。 |
示例:
csharp
// GET /api/products/5?price=100
[HttpGet("{id}")]
public IActionResult GetProduct(
[FromRoute] int id, // 明确指定从路由绑定
[FromQuery] int price) // 明确指定从查询字符串绑定
{
// ...
}
在这种情况下,即使请求中包含同名的表单字段,id 和 price 也只会从指定的源(路由和查询字符串)获取值。如果指定的源中不存在该参数,则会绑定失败(对于非可空类型会报错)。