4、同步API中的校验失败响应设计:HTTP状态码选择(400 Bad Request)、错误响应体结构(code、message、details)
在异步导出流程中,同步API(即发起导出请求的端点)承担着第一道防线的作用。当客户端提交的参数不符合规范时,API必须立即、明确地拒绝请求,而不是将错误参数传递到异步队列中。本节将详细阐述同步校验失败时的响应设计标准,包括HTTP状态码的选择与错误响应体的结构化设计。
4.1 HTTP状态码选择:400 Bad Request
对于参数校验失败,业界公认且最符合HTTP语义的状态码是 400 Bad Request。该状态码表示服务器因请求语法错误、参数格式无效或参数缺失而无法处理请求。在异步导出场景中,以下情况应统一返回400:
- 必填参数缺失:如未提供
export_type或date_range。 - 参数格式错误:如日期格式不是
YYYY-MM-DD,或枚举值不在允许范围内。 - 参数逻辑冲突:如同时指定了
start_date和last_days两个互斥参数。 - 参数超出业务范围:如请求导出的数据量超过单次上限(例如超过10万行)。
设计原则: 不要使用 422 Unprocessable Entity(语义偏语义校验)或 500 Internal Server Error(表示服务端异常)。400 Bad Request 是参数校验失败的标准答案,客户端和中间件都能正确识别。
4.2 错误响应体结构:code、message、details
仅返回状态码不足以帮助客户端快速定位问题。我们设计一个三层结构的JSON响应体,确保错误信息既简洁又具备可操作性:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
code |
string | 是 | 业务错误码,用于程序化判断,如 PARAM_MISSING、PARAM_INVALID、PARAM_CONFLICT |
message |
string | 是 | 面向开发者的可读错误摘要,如 "必填参数 export_type 缺失" |
details |
array | 否 | 参数级错误详情列表,每个元素包含 field、reason、value(可选) |
details 数组中的每个对象结构:
| 字段 | 类型 | 说明 |
|---|---|---|
field |
string | 出错的参数名,如 date_range.start |
reason |
string | 具体错误原因,如 "日期格式必须为 YYYY-MM-DD" |
value |
any | 客户端传入的原始值(可选,用于调试) |
4.3 完整响应示例
以下是一个参数校验失败的完整HTTP响应:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"code": "PARAM_INVALID",
"message": "请求参数校验失败,请检查 details 中的具体错误",
"details": [
{
"field": "date_range.start",
"reason": "日期格式无效,期望格式为 YYYY-MM-DD",
"value": "2024/01/01"
},
{
"field": "export_type",
"reason": "不支持的导出类型,可选值: [csv, xlsx, json]",
"value": "pdf"
}
]
}
最佳实践: 当只有一个参数错误时,
details 数组仍应返回一个元素,保持响应结构的一致性。客户端可以统一遍历 details 数组展示错误,无需区分单错误与多错误场景。
4.4 同步校验流程与响应映射
下图展示了从请求到达、参数校验到错误响应的完整流程:
4.5 常见校验场景与code映射
为方便客户端程序化处理,我们预定义一组标准业务错误码:
| 场景 | HTTP状态码 | code | message 示例 |
|---|---|---|---|
| 必填参数缺失 | 400 | PARAM_MISSING |
"必填参数 export_type 缺失" |
| 参数格式错误 | 400 | PARAM_INVALID |
"参数 date_range.start 格式无效" |
| 参数值超出范围 | 400 | PARAM_OUT_OF_RANGE |
"导出行数超过上限 100000" |
| 参数逻辑冲突 | 400 | PARAM_CONFLICT |
"start_date 与 last_days 不能同时指定" |
| 枚举值不支持 | 400 | PARAM_UNSUPPORTED |
"不支持的导出格式 pdf" |
注意: 不要将业务校验错误(如"该用户无导出权限")映射到400。权限相关错误应使用 403 Forbidden,并采用独立的错误码体系。400 只用于请求参数本身的语法或语义错误。
4.6 设计要点总结
- 状态码统一:所有参数校验失败均使用
400 Bad Request,避免混用 422、406 等非标准状态码。 - 响应体三层结构:
code用于程序判断,message用于快速理解,details用于精确定位。 - details 数组化:即使只有一个错误也返回数组,保持客户端处理逻辑的一致性。
- 原始值回传:在
details.value中回传客户端传入的原始值,便于调试和前端表单回显。 - 错误码可扩展:业务错误码采用大写蛇形命名,团队可在此基础上扩展自定义错误码。