接口设计基础:RESTful 与 gRPC 设计规范
讲讲接口设计吧。金融场景里,接口就是数据的门。门设计得不好,数据就乱跑,或者干脆进不来。我经历过不少项目,接口设计这块坑很多,今天我把 RESTful 和 gRPC 这两套东西掰开揉碎讲清楚。
RESTful 接口设计规范
RESTful 说白了就是「用 HTTP 协议定义资源操作」。资源用 URL 表示,操作用 HTTP 方法表示。就这么简单。但金融场景里得注意几个关键点。
资源路径设计
资源路径要名词复数,动词靠 HTTP 方法覆盖。我见过很多团队在 URL 里塞动词,比如 /api/getUserInfo,乱得不行。
正确示例:
GET /api/privacy/v2/users/{userId}
POST /api/privacy/v2/users/{userId}/consents
PUT /api/privacy/v2/users/{userId}/consents/{consentId}
DELETE /api/privacy/v2/users/{userId}/consents/{consentId}
错误示例:
GET /api/getUserInfo?userId=123
POST /api/saveConsent
PUT /api/updateConsentInformation
注意版本号 v2 放在路径里。金融系统迭代慢但必须向前兼容,路径版控最稳。我在银行项目里吃过没版本号的亏,后来不得不做网关重写。
HTTP 方法选择与幂等性
这个很重要。金融场景的接口必须讲清楚幂等性。我做个表给你们看看:
| HTTP 方法 | 语义 | 幂等 | 金融场景示例 |
|---|---|---|---|
| GET | 查询资源 | 是 | 查询用户隐私授权状态 |
| POST | 创建资源 | 否 | 提交隐私授权申请 |
| PUT | 全量替换 | 是 | 更新用户隐私偏好设置 |
| PATCH | 部分更新 | 否 | 更新用户某个同意项 |
| DELETE | 删除资源 | 是 | 撤销某项隐私授权 |
幂等性不是小事。我曾经遇到过一个扣费服务因为超时重试导致重复扣款,就是因为 POST 不幂等。后来加了一个请求唯一 ID 方案才解决。
数据校验与请求格式
金融接口的数据校验要严格。我一般会在网关层和业务层各做一次校验。请求体用 JSON,但日期、金额这些字段要规定好格式:
- 日期:统一用 ISO 8601,比如
2024-12-01T10:30:00+08:00 - 金额:用整数表示最小单位(分),或者用
decimal字符串 - 敏感字段:请求体里不要出现明文身份证号、卡号
注意:金融场景不要用 application/x-www-form-urlencoded 提交隐私数据。JSON 结构化丢给后端,签名和加密由网关统一处理。
gRPC 接口设计规范
RESTful 在对外暴露接口时很有用。但内部服务之间通信,我更喜欢 gRPC。为什么?性能好,强类型,天然支持流式通信。
.proto 文件就是接口契约
gRPC 的接口定义在 .proto 文件里。这文件既是规范,也是文档,还能自动生成代码。金融场景里,接口一致性是命根子,protobuf 的严格模式恰好满足这一点。
syntax = "proto3";
package privacy.v2;
message QueryConsentRequest {
string user_id = 1;
string request_id = 2; // 幂等用
}
message QueryConsentResponse {
string user_id = 1;
repeated ConsentItem consents = 2;
string timestamp = 3; // ISO 8601
}
message ConsentItem {
string consent_id = 1;
string consent_type = 2; // e.g., "data_share", "marketing"
bool granted = 3;
int64 expire_at = 4; // unix timestamp
}
service PrivacyService {
rpc QueryConsent (QueryConsentRequest) returns (QueryConsentResponse);
rpc UpdateConsent (stream ConsentUpdateRequest) returns (AckResponse);
}
字段编号别乱用。我用 1-15 放高频字段,因为编码后只占 1 字节。16-2047 放低频字段。这是我踩过的坑,一开始没规划,后来改字段编译出来的二进制大了一截。
服务定义与通信模式
gRPC 支持四种模式。金融场景里我常用的有两种:
| 通信模式 | 描述 | 金融场景 |
|---|---|---|
| Unary RPC | 一问一答 | 查询隐私授权状态 |
| Server Streaming | 客户端发送一个请求,服务端持续返回流式数据 | 批量导出隐私日志 |
| Client Streaming | 客户端持续发送数据,服务端统一响应 | 批量更新个人信息 |
| Bidirectional Streaming | 双向实时通信 | 实时隐私风险检测 |
经验之谈:内部服务用 gRPC 时,把超时控制在 5 秒内。金融系统对延迟敏感,gRPC 流式通信如果网络抖动,容易造成批量数据丢失。我在一个日志导出项目里因为没设超时,导致连接一直挂着。
数据格式:JSON vs Protocol Buffers vs FlatBuffers
接口设计绕不开数据格式。我总结了一下三种格式的特点:
| 格式 | 序列化速度 | 数据大小 | 可读性 | 适用场景 |
|---|---|---|---|---|
| JSON | 慢 | 大 | 好 | 对外开放接口、调试 |
| Protocol Buffers | 快 | 小 | 差 | 内部服务调用、高吞吐 |
| FlatBuffers | 极快 | 小 | 差 | 高频交易、移动端 |
为什么会这样?JSON 是文本协议,解析时要建对象树,天生慢。Protocol Buffers 是二进制协议,直接映射结构体。FlatBuffers 连反序列化都省了,直接访问内存。金融场景里,对外接口我坚持用 JSON,调试方便。内部高并发服务,用 Protocol Buffers。实时高频交易,FlatBuffers 是首选。
状态码归纳总结
状态码是接口的「说话方式」。设计不好,调用方猜都猜不到发生了什么。
RESTful 状态码选择
我的经验是不要滥用 200 和 500。很多团队不管结果怎么样都返回 200,然后在 body 里放 code: 10001 表示错误。这样 API 网关做熔断和统计就废了。
| HTTP 状态码 | 含义 | 金融场景使用时机 |
|---|---|---|
| 200 OK | 正常处理完成 | 查询成功、更新成功 |
| 201 Created | 资源创建成功 | 新建隐私授权记录 |
| 400 Bad Request | 请求参数错误 | 请求缺少 user_id、签名无效 |
| 401 Unauthorized | 身份认证失败 | 令牌过期、无权限调用 |
| 403 Forbidden | 无操作权限 | 用户非本人、数据不可达 |
| 404 Not Found | 资源不存在 | 请求的 user_id 或 consent_id 不存在 |
| 409 Conflict | 资源冲突 | 请求幂等 ID 重复 |
| 429 Too Many Requests | 频率限制 | 单位时间内请求数超限 |
| 500 Internal Server Error | 服务端内部错误 | 数据库异常、配置错误 |
| 503 Service Unavailable | 服务暂时不可用 | 网关限流、熔断 |
注意:金融接口里 409 特别重要。幂等机制依赖唯一 ID,如果重复请求来了,别返回 200,返回 409 让调用方自己判断。我曾经见过返回 200 导致下游重复处理业务,差点损失几百万。
gRPC 状态码与 HTTP 映射
gRPC 有自己的一套状态码。通过 gRPC-gateway 转 REST 时,映射关系要提前约定好。
| gRPC 状态码 | HTTP 状态码 | 说明 |
|---|---|---|
| OK (0) | 200 | 正常 |
| InvalidArgument (3) | 400 | 请求参数校验失败 |
| Unauthenticated (16) | 401 | 认证失败 |
| PermissionDenied (7) | 403 | 权限不足 |
| NotFound (5) | 404 | 资源不存在 |
| AlreadyExists (6) | 409 | 资源已存在,幂等冲突 |
| ResourceExhausted (8) | 429 | 限流触发 |
| Internal (13) | 500 | 内部错误 |
| Unavailable (14) | 503 | 服务不可用 |
gRPC 状态码返回时,建议在 trailers 里附带更多错误信息。我习惯在业务错误时额外返回一个 error_details 字段,方便排查问题。
个人习惯:无论用 RESTful 还是 gRPC,我都统一封装一个响应结构。REST 用 { "code": 200, "message": "success", "data": {...} },gRPC 用 error_details.message。这样前端和调用方不用猜状态码的私有含义。
核心知识体系图
下面这张图概括了这一章的核心内容。把 RESTful 和 gRPC 的设计规范、数据格式选择、状态码映射放在一起,你们可以当个快速参考。
接口设计说复杂也复杂,说简单也简单。RESTful 对外,gRPC 对内,数据格式按场景选,状态码按规范走。这套框架用好了,金融系统的接口会非常清晰。记住我说的:接口是门,设计好它,数据才安全。