4.6 模型绑定与输入校验
模型绑定负责把 HTTP 请求中的路由、查询字符串、Header 和 Body 转换为 C# 参数。输入校验负责在业务逻辑执行前拒绝非法数据。
学习目标
- 能区分路由参数、查询参数和请求体绑定。
- 能为请求 DTO 添加校验规则。
- 能返回稳定的 400 错误响应。
- 能理解前端校验和后端校验的职责差异。
应用场景
GET /todos?completed=false&page=1读取查询参数。GET /todos/{id}读取路由参数。POST /todos从 JSON Body 读取创建请求。- 对标题长度、必填字段和分页范围做校验。
核心概念
| 数据来源 | 示例 | 适合内容 |
|---|---|---|
| Route | /todos/{id} | 资源标识 |
| Query | ?page=1 | 筛选、排序、分页 |
| Body | JSON | 创建或更新的复杂对象 |
| Header | Authorization | 认证、追踪、客户端信息 |
案例:分页查询请求
app.MapGet("/todos", async (
[AsParameters] TodoQuery query,
TodoService service,
CancellationToken cancellationToken) =>
{
if (query.Page < 1 || query.PageSize is < 1 or > 100)
{
return Results.BadRequest(new {message = "分页参数不合法"});
}
var result = await service.GetPageAsync(query, cancellationToken);
return Results.Ok(result);
});
public sealed record TodoQuery(
bool? Completed,
int Page = 1,
int PageSize = 20);
示例:创建请求校验
public sealed record CreateTodoRequest(string Title, string? Description)
{
public IReadOnlyDictionary<string, string[]> Validate()
{
var errors = new Dictionary<string, string[]>();
if (string.IsNullOrWhiteSpace(Title))
{
errors["title"] = ["标题不能为空"];
}
else if (Title.Length > 120)
{
errors["title"] = ["标题不能超过 120 个字符"];
}
if (Description?.Length > 500)
{
errors["description"] = ["备注不能超过 500 个字符"];
}
return errors;
}
}
重点难点
- 请求 DTO 是外部输入边界,不应直接复用数据库实体。
- 前端校验只提升体验,后端校验才是安全边界。
- 校验错误要包含字段级信息,方便前端表单展示。
- 分页、排序字段和筛选条件都要设置上限和白名单。
常见误区
| 误区 | 推荐做法 |
|---|---|
| 直接把 Entity 作为请求体 | 使用专门 Request DTO |
| 只校验必填,不限制长度 | 同时校验长度、范围和格式 |
| 前端做了校验,后端就省略 | 后端永远重新校验外部输入 |
练习
- 给任务列表查询增加
sortBy白名单。 - 创建任务时校验标题和备注长度。
- 设计字段级错误格式并让前端展示。
延伸阅读
- ASP.NET Core 模型绑定。
- Minimal API 参数绑定。
- FluentValidation。