RestAPI 设计

RestAPI 设计
MeteorCat在多次项目的大量工程实践之后, 目前采用一下标准响应模式:
- 只有
GET/POST方式做响应, 剔除掉PUT/DELETE方式 - 采用自定义的
*-X-Authorization做 Header 认证参数而不用默认Authorization - 签名采用从参数列表 KEY 正序附加密钥生成签名 MD5
- 支持
i18n多语言必须要 Header 追加*-X-Language标识语言从参数, 默认en英语 - 请求的内容类型为
application/x-www-form-urlencoded传递 - 自定义的
*-X-VersionHeader 版本转发, 版本更新之后接口参数变化需要该参数决定转发新老接口
默认的响应格式 BODY 结构如下:
1 | // HTTP:200, 成功直接返回需要数据结构 |
这种情况请求数据的结构看起来就像以下方式
1 | POST /event/send |
NOVA-X-Authorization 就是自定义 *-X-Authorization 自定义授权头, 用来获取内存的用户数据
注意: {项目标识}-X-Authorization 之类 Header 要设定成动态配置来方便重新设立项目时可以直接复用
参数签名
为了防止参数传递的时候被拦截并且修改, 所有带有参数的请求必须带有以下参数
-
time: UTC毫秒时间戳, 无视时区的全球通用时间戳, 用于保证请求实效性(一般30~60s内) -
sign: 参数签名MD5哈希, 用于确保所有参数没有被修改过, 如果无法匹配就代表参数非法
这里有个前提就是你的接口服务是 多应用还是单应用 模式?
-
单应用: 客户端和服务端直接约定 APP_KEY 即可, 直接用该 KEY 对参数做签名
-
多应用: 需要在管理后台动态创建应用并且生成单独 APP_ID 和 APP_KEY 来做标识应用的参数签名
这里先讲解单应用的请求流程, 后面再扩展多应用的请求方式
单应用一般应用于公司独有不对外的项目服务, 直接内部预定好拿到 APP_KEY 做参数签名, 比如下面所示
1 |
|
之后客户端提交的 HTTP 参数请求如下
1 | POST /user/login |
而服务端的需要处理流程也比较方便
-
提取
time字段和服务器判断 UTC 时间戳误差是否大于 30~60s 之中 -
提取
sign字段对请求参数重新做 KEY 正序排序再用 APP_KEY 验证MD5哈希签名
这种方式就可以用来处理大部分日常请求的问题, 另外还有版本升级的问题
多应用服务
带有多应用性质一般是要对外发布的通用接口服务, 比较常见的场景就是对外游戏发行SDK的情况.
这里也是采用 Header 自定义头标识指定应用即可, 比如以下方式:
-
自定义的
*-X-AppHeader 传入系统内部的应用唯一标识 -
应用对外标识不推荐使用数据库表的 ID, 会被拿着 int 值遍历导出旗下所有应用数据
-
管理后台创建应用时, 都会创建 APP_ID 和 APP_IDENT, 前者为 int 值内部使用, 后台随机字符串外部使用
比如这种按照上面的参数签名方式多应用请求方法其实也不需要做太大修改:
1 | POST /user/login |
NOVA-X-App 就是自定义 *-X-App 规则的 Header, 而服务端只需要做以下处理
-
从 Redis/MySQL 取出对应 APP_IDENT 的应用信息, 不存在就抛出 404 错误
-
从获取的应用数据结构提取出 APP_KEY 对参数签名做 MD5 哈希验证处理
这种就是多应用的接口处理方式, 其实也和单应用差不多, 只有请求头附加上对应用标识索引对应 APP 而已
多语言翻译
如果采用你的接口采用面向全球化服务, 那么就需要注意错误和提示返回的文本必须大务符合对应语言文本.
客户端是能获取设备系统语言, 但是应该以服务端的语言字段标识为准, 客户端本地语言需要自己做关联
这里提供一份日常语言 i18n 支持表, 一般需要实现以下语言表功能:
| 标识 | 备注 |
|---|---|
| en | 英语, 系统默认的语言 |
| zh-CN | 简体中文 |
| zh-TW | 繁体中文 |
| ja | 日文 |
| ko | 韩语 |
采用 BCP-47 本地化的扩展实现, 以上语言表选取实现即可, 如果不支持就默认返回 en
具体的本地化规范: https://tools.ietf.org/html/bcp47
一般采用国际规范的 BCP-47 标识传递 *-X-Language 的 Header 即可, 多应用+本地化 请求如下:
1 | POST /user/login |
这里的 NOVA-X-Language 就是自定义的 *-X-Language 规则 Header
版本升级
版本升级并不是接口必选项, 只有要求应用不强制更新的情况才需要用到
比如某个接入SDK的应用v1.0.1版本修改了字段名, 老版本应用不设置强制升级的情况
这种情况下就需要声明 *-X-Version 版本调度, 这里其实挺有意思, 服务端可以按照以下方式选择性处理
-
反向代理: 如果接口变动很多, 直接独立成两个项目利用从 Nginx 层面转发到不同两个项目入口
-
内部转发: 通过识别 Header 来做业务层面的请求处理, 也就是自己去处理内部版本代码
这部分见仁见智, 其实个人觉得直接分出划新目录让 Nginx 来识别 Header 转发到指定老项目其实更简洁
需要在 Git 版本库上面同步版本号 tag 方便迁移部署
靠 Header 识别版本转发到对应新老项目请求入口, 避免在原来项目上做 屎上雕花 的操作
而 Nginx 配置配置也十分简单:
1 | map $http_nova_x_version $api_backend { |
后续的请求好在地址会各自转分到对应项目之中
-
{NOVA-X-Version=v1}: 转发到 http://api-v1, 对应本地 /www/nova/v1, 默认也是在该链路地址
-
{NOVA-X-Version=v2}: 转发到 http://api-v2, 对应本地 /www/nova/v2
Header 即路由, Nginx 即调度器, Git Tag 即版本版本地址

