推荐简单业务场景(CRUD为主)使用 Restful 风格,业务复杂场景借鉴 Restful 的优点(抽象所有资源),以更容易理解业务。 |
🔗 规范约束
统一响应体格式
返回值统一为json格式,最外层只包含 code
、msg
、data
三个字段,接口返回包含数据内容时,仅使用 data
字段。
{ "code":"错误码,成功为0", "msg":"返回的(错误)描述信息,不能有堆栈信息", "data":{数据} }
统一 API 文档
为了便于对接,组件 doc
目录下须包含以下文件
-
api.json
-
统一使用 swagger json 文档
-
需要指定 info-title, description,version,contact
-
basePath
对应 context 值,格式为:/<appId>
-
-
-
api.md
-
api文档,包含如下,不描述则采用本文的推荐设计:
-
简介
-
认证
-
鉴权规则
-
安全传输与加密算法
-
返回值规则
-
空响应
-
单值响应
-
多值响应(分页)
-
多值响应(不分页)
-
-
-
ext.md
-
swagger 返回值无法描述泛型等,额外标注说明
-
-
guidance.md
-
编程指引
-
-
appendix.md
-
附录,错误码、数据字典、翻译项、复杂参数解释
-
-
<appId>-api-sdk.jar
-
接口sdk
-
请求头约束
请求头保留字段
参数名 | 类型 | 必选 | 说明 |
---|---|---|---|
User-Agent |
string |
√ |
终端类型 |
Token |
string |
√ |
接口认证使用:身份认证信息,采用 base64 编码。 |
X-Sid |
string |
× |
秘钥协商使用:安全会话 ID。 |
X-Dk |
string |
× |
秘钥协商使用:使用协商密钥加密的 |
User-Id |
string |
× |
用户 ID |
Trace-Id |
string |
√ |
用于标识一笔业务的唯一序号,UUID 格式 |
Span-Id |
string |
√ |
用于标识某一阶段调用序号,32位字母或数字 |
Biz-Id |
string |
× |
业务标识 |
X-B3-TraceId |
hex string |
√ |
UUID 格式 |
X-B3-SpanId |
hex string |
√ |
64 位值,采用小写 16 进制字符显示 |
X-B3-ParentSpanId |
hex string |
√ |
64 位值,采用小写 16 进制字符显示 |
X-B3-Sampled |
boolean |
× |
|
X-B3-Flags |
string |
× |
|
请求参数要求
参数须提供 取值范围
或 格式限制
或 枚举限制
时间字段和格式
-
当查询条件需要使用起止时间时,参数名统一采用
beginTime
和endTime
。 -
采用
ISO8601
格式,使用带 Time-zone 的标准时间格式,如:2020-7-13T00:00:00.000+08:00
;
字符串和编码
-
字符串的所有操作统一使用 UTF-8 编码。
-
长度限制如下
场景 | 推荐长度 |
---|---|
ID、标识、名称、别名 |
64, 128 |
描述、备注、文件名(全路径) |
512, 1024 |
详情 |
2048 |
文本、文件 |
不限制 |
常用字段约束
统一常用的参数名称
分页查询参数
demo: 每页20条记录,查询第一页,先按照 username
正序排序,再按照 phoneNo
逆序排序
{ "pageNo":"1", "pageSize":"20", "conditions": [ { "property": "username", "like": "cn" },{ "property": "level", "equal": "vip" } ], "orderBy":[ { "property": "username", "direction": "asc" },{ "property": "phoneNo", "direction": "desc" } ] }
用途 | 参数名 | 举例 |
---|---|---|
查询页码 |
pageNo |
1 |
分页大小 |
pageSize |
20 |
排序参考 |
sortBy |
["name"] |
排序规则 |
order |
"asc" |
分页查询参数名称
安全传输
参考 密钥协商 |
编码约束
-
定义统一返回值类
BaseResponse
,且该类只能在接口层使用 -
所有 Controller 层接口函数统一返回
BaseResponse
,或使用全局返回值自动包装 -
入参,返回值尽量不要有
Map
类 -
Controller 层进行
DTO
转换,不允许将DTO
传递倒业务层 -
Controller 不要出现 Request,Response 这类对象
-
一般不需要在这里打印日志,异常处理,使用统一的日志打印
-
注意防枚举(id有顺序)、防重放
Ajax
VS Restful
-
虽然两者都是 HTTP协议 JSON 格式
-
Restful 属于 RPC 的一种形式,职责为服务间通信,通常是无状态的。如查询天气接口,接口校验主要为 token 认证。
-
Ajax 是前后端交互的一种方式,通过异步请求,实现页面局部刷效果,提升用户体验的。通常是有状态的接口,如获取当前用户点赞数;在服务端允许跨域访问时,也可以调用一些无状态的接口,如自己的博客里嵌入了查询天气的模块。
HTTP Status: Restful
VS 返回200,同时返回code字段
两种优秀的接口格式并不冲突,完全可以一起用,HTTP Status 不返回200,如500,一样可以带响应 body,只是浏览器默认认为 2xx
才是成功,JavaScript 中完全可以通过自定义代码处理非 2xx
响应的内容
建议同时使用,自定义返回值状态码便于具体问题排查,同时也用好 HTTP Status 来提高排查效率:
-
响应码
200
表示接口正常返回。 -
4xx
表示客户端错误,前端开发先排查。 -
5xx
表示服务端异常,后端开发先排查。 -
2xx
但body包含错误码,说明需要前端处理该错误码并做特殊处理或展示。
参考业界多家知名互联网企业的设计,各企业在此处抉择如出一辙。 |
方案一:只用HTTP状态码来表示状态【不推荐】
-
200时候返回内容就是数据,最多有个分页
分页也可以像 github 那样 放到header,响应内容更整洁 |
-
只有在抛出Exception异常(即使业务逻辑上有问题,也抛出APIException异常)才返回像下面这样的响应:
HTTP/1.1 405 Method Not Allowed Content-Type: application/json {"status_code": 405, "message": "Method 'DELETE' not allowed."}
方案二:所有接口都返回200【可用】
响应体里包含:自定义码值、信息、数据
HTTP/1.1 200 Success Content-Type: application/json { "code": 405001 "message": "xxxxx" "result": null }
方案三:同时使用 HttpStatusCode + 自定义错误码【推荐】
响应体里包含:自定义码值、信息、数据
HTTP/1.1 405 Method Not Allowed Content-Type: application/json { "code": 405001 "message": "xxxxx" "result": { "id": 1 ... } }
方案优缺点详细对比
-
方案一
-
优点
-
作为服务端更倾向于方案一,因为大多框架和各家提供的API都是这么干的,其实也很简单(先看状态码,然后直接根据状态码决定后续动作)
-
使用本身通讯协议作为语义,更符合该种协议的约定
-
有利于中间层对请求进行缓存
-
客户端进行相应封装后,代码都好维护,结构整洁
-
-
缺点
-
可能有奇葩运营商对某些HTTP状态码进行一些自定义行为(https可规避),如非 200 可能有广告
-
可能有业务异常较多,是 HTTP status 无法描述的,或不足以描述的,如登录失败 401 可能是账号不存在(引导注册),也可能是密码错误(清空密码框重新输入)
-
可移植性较差,当不是 HTTP 通信时,需要推翻所有客户端逻辑
-
某些技术欠佳、不负责任的客户端开发会认为,只要不是200一定是后端问题,认为接口不稳定
-
需要所有服务端开发者都能完全理解并实现 RESTful API,且客户端开发人员也能理解
-
非 200 响应,可能会被浏览器拦截处理
-
-
-
方案二
-
优点
-
一些客户端开发希望是方案二,因为这样会让客户端的处理逻辑变简单(200以外全去捕获异常,200时再看
code
做不同处理) -
API响应码值语义分层明确,HTTP status 是为协议层的使用的,API 中可以自定义语义状态,互不影响
-
早些年,普遍都是这样做,符合习惯,兼容旧系统
-
一些客户端/前端/APP开发不懂 HTTP 协议,不知道状态码是什么,这样做更直白
-
一些客户端框架,响应不是200则抛异常,要么显示捕获、要么就得深入了解框架的设计,且该框架支持扩展
-
-
缺点
-
即使自定义了码值,仍然复用了 HTTP status 的 200
-
服务器端不一定真能保证总是返回200的状态码,一些框架中原始就抛出 401 / 403 / 404 / 500 等异常,需要服务端开发改造来规避
-
-
💫 相关知识说明
Restful HTTP Method 说明
类型 |
说明 |
GET |
从服务器取出资源 |
POST |
在服务器新建一个资源,修改一个资源或者删除一个资源 |
PUT |
在服务器更新资源(客户端提供改变后的完整资源) |
DELETE |
从服务器删除资源 |
HEAD |
获取资源的元数据 |
PATCH |
在服务器更新资源(客户端提供改变的属性) |
OPTIONS |
获取信息,关于资源的哪些属性是客户端可以改变的 |
常用 HTTP 状态码
HTTP状态码 | 说明 |
---|---|
200 OK |
服务器成功返回用户请求的数据。处理成功时默认该值。 |
201 Created |
修改数据成功(返回之前已经入库) |
202 Accepted |
表示一个请求已经进入后台排队(异步任务/如批量异步删除) |
204 No Content |
表示请求成功但无返回值,可用于修改,删除 |
301 Moved Permanently |
资源的URI已被永久更新 |
302 Found |
资源URI重定向,与301类似(临时) |
304 Not Modified |
资源未更改,客户的缓存资源是最新的, 请客户端使用缓存(缓存),ES-ik插件的动态更新用到了这个 |
400 Invalid Request |
用户发出的请求有错误,是无效请求,如字段映射不正确,参数不正确都可以用这个 |
401 Unauthorized |
需要先进行认证(登录) |
403 Forbidden |
服务器拒绝执行,一般是由于权限不够,也可能ip进了黑名单等 |
404 Not Found |
用户发出的请求是针对不存在的记录 |
405 Method Not Allowed |
不允许执行目标方法,如只允许POGT却使用GET,响应中应该带有 Allow 头,内容为对该资源有效的 HTTP 方法 |
406 Not Acceptable |
用户请求的格式不可得,如服务器返回 |
410 Gone |
用户请求的资源被永久删除,且不会再得到 |
422 Unprocesable entity |
参数格式正确,但语义错误(创建资源时,校验未通过),浏览器将它与400处理方式相同,因此也可以使用400 |
429 Too Many Request |
请求过多,触发限流时,可用于反爬虫,418(触发彩蛋)也可用于反爬虫 |
500 Internal Error |
服务器发生错误,用户将无法判断发出的请求是否成功 |
503 Service Unavailable |
服务不可用状态,多半是因为服务器问题,例如CPU占用率大,等等 |