4. 请求与响应规范:统一请求格式、统一响应结构、HTTP状态码选择、错误码设计
这一章,我们来聊聊API的「门面」问题。
你想想看,一个风控系统每天要处理几百万甚至上千万次请求。如果每个接口的请求格式都不一样,响应结构也五花八门,那维护起来简直就是噩梦。我早年接手过一个老系统,光是解析不同接口的返回就写了十几个适配器——嗯,那滋味真不好受。
4.1 统一请求格式
先说请求。我个人习惯把所有风控接口的请求体统一成一种结构。别小看这个「统一」,它能让你的SDK、网关、日志解析全都省心不少。
我推荐的结构是这样的:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": 1712563200000,
"appId": "risk_engine_v3",
"data": {
// 业务参数放这里
},
"ext": {
// 扩展字段,比如traceId、客户端IP等
}
}
这里有几个关键点:
- requestId:全局唯一,用于链路追踪。我建议用UUID,别用自增ID,否则分布式下容易撞车。
- timestamp:客户端时间戳,服务端可以用来做防重放校验。
- appId:调用方标识,方便做鉴权和限流。
- data:真正的业务参数。这样设计的好处是,无论业务怎么变,外层结构纹丝不动。
小技巧:我在项目中遇到过,有些团队把签名sign字段也放在最外层。我个人建议放在ext里,或者单独一个sign字段,别跟data混在一起。否则验签时还得先解析data,逻辑上绕。
4.2 统一响应结构
响应结构比请求更重要。为什么?因为调用方拿到响应后,第一件事就是解析它。如果结构不统一,每个接口都得写一套解析逻辑,那前端和下游系统会骂娘的。
我常用的响应结构长这样:
{
"code": 0,
"message": "success",
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"data": {
// 业务返回数据
}
}
注意几个细节:
- code:业务状态码,0表示成功,非0表示各种错误。别用HTTP状态码直接当业务码,后面会细说。
- message:人类可读的描述。注意,这里不要暴露敏感信息,比如「数据库连接失败」这种话,对外返回「系统繁忙」就好。
- requestId:回传请求ID,方便排查问题。我曾经靠这个字段在日志里定位过一个诡异的超时问题,省了整整两天时间。
- data:真正的业务数据。如果请求失败,data可以是null或者空对象。
避坑指南:我曾经见过一个系统,成功时返回data是对象,失败时返回data是字符串。结果前端每次都要写if(typeof data === 'string')... 这种设计,说白了就是给自己挖坑。统一!统一!统一!
4.3 HTTP状态码选择
HTTP状态码怎么选?很多新手喜欢把所有成功都返回200,所有失败都返回500。这其实不太对。
我个人的原则是:HTTP状态码只表示「通信层」的状态,不表示「业务层」的状态。
举个例子:
| 场景 | HTTP状态码 | 说明 |
|---|---|---|
| 请求成功,业务处理成功 | 200 | 正常返回,code=0 |
| 请求成功,业务处理失败 | 200 | 正常返回,code≠0 |
| 参数校验失败 | 400 | Bad Request,客户端问题 |
| 鉴权失败 | 401 | Unauthorized |
| 无权限 | 403 | Forbidden |
| 资源不存在 | 404 | Not Found |
| 请求频率过高 | 429 | Too Many Requests |
| 服务端内部错误 | 500 | Internal Server Error |
你看,业务上的「拒绝交易」其实还是返回200,只是code变成了10001。这样网关层可以根据HTTP状态码做简单的熔断和限流,而业务层根据code做具体的逻辑判断。各司其职,清晰明了。
4.4 错误码设计
错误码设计,这是门学问。设计得好,排查问题像喝水一样简单;设计得烂,查个错误得翻半天文档。
我推荐用分层编码的方式。举个例子:
错误码格式:A-BB-CCC
A:错误级别
1 - 系统级错误
2 - 业务级错误
3 - 参数级错误
BB:模块编号
01 - 认证模块
02 - 规则引擎
03 - 数据源模块
...
CCC:具体错误编号
001 - 001 范围内的具体错误
实际用起来是这样的:
| 错误码 | 含义 | 说明 |
|---|---|---|
| 10001 | 系统内部错误 | 未知异常,需要人工介入 |
| 10101 | 数据库连接失败 | 系统级,数据源模块 |
| 20001 | 交易被风控规则拦截 | 业务级,规则引擎 |
| 20101 | 用户命中黑名单 | 业务级,规则引擎 |
| 30001 | 必填参数缺失 | 参数级,通用 |
| 30101 | 手机号格式错误 | 参数级,认证模块 |
这样设计的好处是:
- 看一眼错误码,就知道是哪个级别、哪个模块出的问题。
- 新增错误码时,不会跟已有的冲突。
- 客户端可以根据错误码前缀做不同的处理策略,比如3开头的直接提示用户修改输入,2开头的展示风控结果页面。
核心原则:错误码一旦发布,尽量不要修改含义。我见过一个团队把10001从「系统繁忙」改成「余额不足」,结果上游系统全部按「系统繁忙」做了重试,导致大量重复扣款。嗯,那场面...你懂的。
4.5 知识体系总览
说了这么多,咱们用一张图把这一章的核心逻辑串起来:
这张图把四个核心要点串在了一起。你看,从请求进来,到响应出去,中间经过状态码和错误码的规范,整个链路就清晰了。说白了,规范不是为了限制你,而是为了让你和你的团队少踩坑。
好,这一章就到这里。记住:规范是给未来留的退路。你现在偷的懒,将来都会变成加班还的债。