第3章 请求与响应结构规范:统一报文格式、必选/可选字段、分页与排序参数设计
3.1 统一报文格式设计原则
支付API的所有请求与响应必须遵循统一的报文结构,降低客户端与服务端的对接成本。统一报文格式包含三个核心层级:传输层(HTTP方法、路径、状态码)、头部层(公共请求头与响应头)、业务体层(JSON载荷)。
设计目标:任何第三方开发者在阅读一份API文档后,无须猜测字段含义,仅凭报文结构即可完成首次联调。
下面是一份标准请求体的JSON骨架:
{
"requestId": "202510231534280001", // 全局唯一,用于链路追踪
"timestamp": 1698058468000, // 毫秒级Unix时间戳
"version": "1.0", // API版本号
"bizContent": { // 业务参数容器
// 具体业务字段
},
"sign": "A7B3...F9E2" // 签名值(可选,由服务端配置决定)
}
统一响应体骨架:
{
"code": "00000", // 业务状态码:00000=成功
"message": "success", // 提示信息
"requestId": "202510231534280001", // 回显请求ID
"timestamp": 1698058468001,
"data": { // 成功时返回数据
// 具体业务字段
},
"errorDetail": {} // 失败时返回错误细节(可选)
}
3.2 必选与可选字段规范
每个字段必须明确标注 M(必选 Mandatory)、O(可选 Optional)或 C(条件必选 Conditional)。条件必选字段通常关联特定场景,例如退款接口中 refundAmount 为必选,而 refundReason 为可选。
| 字段名称 | 类型 | 必选 | 说明 |
|---|---|---|---|
| requestId | String(32) | M | 客户端生成,保证幂等 |
| timestamp | Long | M | 请求发起的毫秒时间戳 |
| version | String(8) | M | API版本号,如1.0 |
| sign | String(64) | C | 签署模式开启时必选 |
| bizContent | Object | M | 业务参数,内部字段按业务文档定义 |
常见错误:将条件必选字段错误标记为必选,导致非场景客户端持续报错;或标记为可选,但服务端实际强制校验,引发签名失败或字段缺失异常。
3.3 分页与排序参数设计
列表类接口必须支持分页与排序,遵循RESTful风格。分页参数统一放在请求的 query string 中,排序参数也一并处理。
3.3.1 分页参数标准
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| page | Integer | 1 | 当前页码,从1开始 |
| pageSize | Integer | 20 | 每页记录数,最大不超过100 |
| sort | String | createTime | 排序字段名,支持驼峰映射 |
| order | String | desc | 排序方向:asc/desc |
3.3.2 排序字段白名单
为防止SQL注入排序字段,服务端必须维护排序白名单。仅允许白名单内的字段参与排序,非法字段直接返回参数校验错误。
// Java 枚举示例
public enum SortField {
CREATE_TIME("createTime"),
TRANSACTION_AMOUNT("transactionAmount"),
PAY_STATUS("payStatus");
private final String dbColumn;
SortField(String dbColumn) { this.dbColumn = dbColumn; }
public static boolean isValid(String field) {
return Arrays.stream(values())
.anyMatch(f -> f.dbColumn.equals(field));
}
}
3.3.3 分页响应结构
统一响应中 `data` 字段必须包含分页元数据 + 列表数据:
{
"code": "00000",
"message": "success",
"requestId": "202510231534280001",
"data": {
"totalRecords": 156, // 总记录数
"totalPages": 8, // 总页数
"currentPage": 1,
"pageSize": 20,
"list": [ // 当前页数据
{ "orderId": "O202510230001", "amount": 99.00 },
{ "orderId": "O202510230002", "amount": 188.00 }
]
}
}
性能建议:分页查询使用“游标分页”替代传统 offset 分页时,可在响应中增加
nextCursor 字段,由客户端携带继续请求。本课程第5章将展开该优化方案。
3.4 报文结构流程概览
下图展示从请求组装到响应解析的完整数据流:
3.5 常见错误与调试方法
- 分页参数非法:page 值小于1或 pageSize 大于最大值时,服务端应返回
"code": "40001"+ 明确错误提示。 - 排序字段被注入:客户端传入
sort=password或sort=1;DROP TABLE,服务端必须拦截并返回参数校验失败。 - 必选字段缺失:服务端应一次性校验所有必选字段,而非逐个报错,减少客户端重试次数。
- 响应体不统一:成功时 data 字段可能返回 null 而非空对象,导致客户端解包异常。约定成功时 data 为 JSON 对象或数组;失败时 data 不出现或为 null。
3.6 配置示例(Spring Boot 过滤器实现统一结构)
@Component
public class UnifiedResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, ...) {
if (body instanceof UnifiedResponse) return body;
return UnifiedResponse.success(body);
}
}
上述过滤器将所有正常返回包装为统一报文格式,而异常由 @RestControllerAdvice 统一捕获并返回 UnifiedResponse.error(code, msg)。
版本兼容提醒:当 API 版本升级(如从 1.0 升至 2.0)时,若分页参数名称或响应结构发生变化,必须在请求体中通过
version 字段做路由,避免破坏已有客户端。推荐在网关层配置版本路由,不同版本解析不同的参数模型。
3.7 本章小结
统一报文格式是支付API稳定性的基石。本章重点包括:
- 请求与响应采用固定 JSON 骨架,包含 requestId、timestamp、version、bizContent、sign 等通用字段。
- 必选 / 可选 / 条件必选字段通过文档或注解明确标记,服务端统一校验。
- 分页参数使用 page / pageSize / sort / order,服务端必须做排序白名单校验。
- 分页响应包含 totalRecords、totalPages、currentPage、pageSize、list 五个元数据。
- 通过全局 ResponseBodyAdvice 实现响应格式统一,减少重复代码。
实践作业:请在你的项目中实现一个统一报文格式的过滤器/拦截器,并编写一个带有分页排序的订单列表接口,测试非法排序字段是否被拦截。