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 知识体系总览

说了这么多,咱们用一张图把这一章的核心逻辑串起来:

请求与响应规范 统一请求格式 • requestId (链路追踪) • timestamp (防重放) • data/ext 分离设计 统一响应结构 • code + message + data • 成功/失败结构一致 • 不回传敏感信息 HTTP状态码选择 • 200: 通信成功 • 400/401/403/429/500 • 业务状态看code,不看HTTP 错误码设计 • 分层编码: A-BB-CCC • 系统级/业务级/参数级 • 发布后不改含义

这张图把四个核心要点串在了一起。你看,从请求进来,到响应出去,中间经过状态码和错误码的规范,整个链路就清晰了。说白了,规范不是为了限制你,而是为了让你和你的团队少踩坑。

好,这一章就到这里。记住:规范是给未来留的退路。你现在偷的懒,将来都会变成加班还的债。


专注资料整理