5.1 DTO 与 API 契约
本章目标: 明确前后端通过 HTTP 和 JSON 协作,约定成功与失败的结构。
以实际 JSON 为准
public record TaskDto(int Id, string Title, bool IsDone);
public record CreateTaskRequest(string Title);
public record UpdateTaskRequest(bool IsDone);
ASP.NET Core 使用默认 Web JSON 选项时,响应通常为:
{"id":1,"title":"学习 Vue","isDone":false}
interface TaskItem { id: number; title: string; isDone: boolean; }
interface CreateTaskRequest { title: string; }
interface UpdateTaskRequest { isDone: boolean; }
前端读取 isDone,不能因为 C# 属性叫 IsDone 就照搬大小写。自定义 JSON 选项、JsonPropertyName 或第三方响应包装都可能改变契约。
先列出接口
| 方法 | 路径 | 请求体 | 成功结果 |
|---|---|---|---|
| GET | /api/tasks | 无 | 200 + 任务数组 |
| GET | /api/tasks/{id} | 无 | 200 + 单条任务,或 404 |
| POST | /api/tasks | { "title": "..." } | 201 + 新任务 |
| PUT | /api/tasks/{id} | { "isDone": true } | 200 + 更新后的任务 |
| DELETE | /api/tasks/{id} | 无 | 204,无正文 |
本手册实战的 PUT 表示“设置完成状态”,DTO 特意只包含 isDone,不是通用任务编辑接口。生产项目可按团队约定改为 PATCH,但前后端必须一起调整。
记录不存在返回 404,输入无效返回 400。前端发送状态设置值,比“服务端收到一次就翻转一次”更容易处理重复提交。
统一错误格式
ASP.NET Core 的 ProblemDetails / ValidationProblemDetails 能表达结构化错误,例如:
{
"title": "One or more validation errors occurred.",
"status": 400,
"errors": { "title": ["任务名称不能为空"] }
}
前端优先展示字段 errors,其次 detail,再其次 title。最终保留 HTTP 状态码作为兜底。不要把数据库异常堆栈直接返回给用户。
带 [ApiController] 的 Controller 常提供自动模型验证。Minimal API 是否自动执行 DataAnnotations 校验取决于 .NET 版本及配置。本手册示例显式校验标题,避免依赖隐含行为。
类型映射中的边界
| 后端值 | JSON / 前端建议 | 原因 |
|---|---|---|
int 正整数 ID | number | 位于 JS 安全整数范围内 |
大 long ID | string | 大于 2^53 - 1 时可能丢精度 |
decimal 金额 | 约定十进制字符串或最小单位整数 | JS number 是二进制浮点 |
DateTimeOffset | ISO 8601 字符串 | 不会自动变成 JS Date,需明确时区 |
| 枚举 | 明确约定数字或字符串 | JSON 转换器设置会影响结果 |
| 可空属性 | T | null,按契约可能另有缺省 | null 与字段未返回不是完全一样 |
分页契约
public record PageResult<T>(IReadOnlyList<T> Items, int Total, int Page, int PageSize);
interface PageResult<T> {
items: T[];
total: number;
page: number;
pageSize: number;
}
明确页码从 0 还是 1 开始,最大 pageSize,排序稳定性和空页行为。前端本地筛选当前页不能替代服务端对全部数据的筛选。
已使用 Furion 的项目
Furion 可能按配置对响应进行统一包装。观察真实响应后决定是否在 API 模块拆出业务 data,别把本手册的“裸数组”写法直接用于所有 Furion 项目。
例如实际返回 { code, data, message } 时,应检查 HTTP 层和业务 code,再验证 data。统一处理放在 API 层,不让每个组件各猜一次返回格式。
练习
在 Network 中记录实战项目 GET、POST、DELETE 的成功状态与正文。把 TaskItem.id 暂改为 string,观察编译器是否能自动验证服务器真的返回了字符串。
检查答案
成功状态分别为 200、201、204,DELETE 没有正文。只改 TS 类型不能改变后端 JSON,也不能生成运行时验证;需要实际请求检查和边界验证。