开发实践:一份复杂业务系统的 RESTFul 接口规范
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
1. 从需求入手
-
对象增删改查
-
对象列表获取
-
对象的复杂处理挖掘、整理、汇总
2. 资源分类
-
对象型
**/project/1
-
列表型
**/projects
-
算法型
**/project/search?input=**
3. 设计 URI
3.1. URI 命名
-
有意义的单词全部小写
-
多个单词时使用下划线分隔设计良好的 URI 通常会避免多个单词
-
不推荐使用缩写
3.2. URI 结构
几点
-
路径变量表示层次结构
-
路径标点表示非层次结构
3.2.1. 路径变量
id为8的deal的评论列表
/deal/8/comments
id为8的商家下第88条评论
/merchant/8/comment/88
上述通过实体关联关系进行路径导航一般是 id 导航如果实体之间关联层级过深则使用查询参数替代路径层级导航。
-
路径层级GET /zoo/1/area/2/animal/3
-
查询参数GET /zoo/1?area=2&animal=3
组合实体的访问必须通过父实体的 id导航访问。组合实体不是first-class的实体它的生命周期完全依赖父实体无法独立存在在实现上通常是对数据库表中某些列的抽象不直接对应表也无id。
3.2.2. 路径标点
”.”表达不同的representation约定 Response 返回的数据格式
-
/deal/6.json
-
/deal/8.xml
3.3. URI 对外暴露的方法
GET获取服务器的资源
-
安全性服务器端响应 GET 请求并不会对服务器造成副作用
-
不要使用 GET 方法来修改服务器端的状态信息
POST创建资源
-
创建从属资源
-
客户端无法确定对应 URI
-
POST 请求不安全也不是幂等的因此发布 POST 请求需要小心
备注幂等多次操作与只进行一次的效果相同。
PUT创建资源和更新资源
-
与 POST 创建资源的区别客户端知道 URI具有幂等性
-
在网络环境差的情况下可将 POST 创建资源重构成 PUT 创建资源
DELETE删除资源
-
资源划分粒度要使得删除资源不用带参数
-
具备幂等性
HTTP 请求返回码
-
建议HTTP 头部的返回码统一用200OK具体状态在 body 中使用参数标识
-
body 中返回码的值与 HTTP 协议中返回码的值保持一致便于大家统一理解
HTTP 常见状态码
-
操作成功2xx200ok201created
-
Client Error4xx400bad request401未授权404not found
-
Server Error5xx500internal server error
小结
RESTRepresentational States Transfer表现层状态转化 & 有状态传输本质上REST 关注于资源将所有的内容看做一个资源图片、文本、计算为每一个资源分配唯一的地址并对这些资源进行规范的操作。
几个方面
-
资源的地址URIURLURI 是抽象URL 是具体实现
-
资源的形式实体文件如图片文本htmljson。甚至一些算法及服务
-
对资源的操作GET\POST\PUT\DELETE
4. URL 补充
避免层级过深的 URL/ 在 URL 中表示层级按实体关联关系进行对象导航通常是 id 导航。
过深的 URL 层级导航容易造成 URL 迅速膨胀例如GET /zoo/1/area/2/animal/3推荐使用查询参数替代路径中的实体导航例如GET /zoo/1?area=2&animal=3
5. 请求及返回内容的规范
几点
-
json和xml对象的属性必须 使用驼峰法命名一个单词时全部小写多个单词时第一个单词小写后续的单词首字母大写
-
单个对象对象包含在 data 元素中{}
-
多个对象data 是一个 list[{},{},…]paging 包含分页信息{offset,limit,total}
-
错误与异常error 元素中包含{code, type, message}
1. `// 单个对象`
2. `{`
3. `"data" : {`
4. `"bdId" : 8,`
5. `"bdName": "Rongjun",`
6. `"commission" : 1200.00,`
7. `...`
8. `}`
9. `}`
11. `// 多个对象`
12. `{`
13. `"data" : [`
14. `{`
15. `"code" : "1234567890",`
16. `"status": 0,`
17. `},`
18. `{`
19. `"code" : "234578901",`
20. `"status": 128,`
21. `}`
22. `...`
23. `]`
24. `"paging" : {`
25. `"offset" : 0,`
26. `"limit" : 20,`
27. `"total" : 100`
28. `}`
29. `}`
31. `// 错误信息`
32. `{`
33. `"error" : {`
34. `"code" : 401, /* code 仅用于表示有错误相同的 code 可能有不同的 type 和 message */`
35. `"type" : "PermissionDenied", /* type表示真正的错误类型错误类型的唯一标示 */`
36. `"message" : "抱歉你没有足够的权限" /* 错误对应的详细说明和type成对。可以理解type是titlemessage是body */`
37. `}`
38. `}`
POST/PUT 提交的请求数据
-
当请求方法为POST或PUT时通常需要在Request Body中传递数据。
-
Request数据格式Incoming Representation可以根据需要采用url form或json格式。
-
建议优先考虑采用json格式此时HTTP Header的“Content-Type”设置为”application/json”。
-
相比于Response的格式定义考虑到Request body中传递的都是业务数据不需要用data来和其他信息做区别建议采用 {x:1, y:2} 的格式。
疑问Request 通过 POST 方式传递数据时如何定义数据的编码格式
6. 猫眼演出接口约定
整体上遵循技术文档-前后端约定补充一些
-
B 端 API 接口/api/admin 开头.json 结尾
-
C 端 API 接口/api开头.json 结尾
-
C 端 Web 接口实际对象实体开头
7. 异常处理
出现异常时要满足两种场景
-
开发调试时方便看到完整的异常信息方便调试
-
线上服务时屏蔽异常细节信息只需向用户显示提示信息
技术上两种情况
-
请求 Web 页面出现异常或者 model 中属性异常
-
调用 API 端口出现异常通常是 Ajax 请求对应的 JSON 数据
从业务与非业务角度异常分为两类
-
业务异常自己的业务代码抛出表示一个用例的前置条件不满足、业务规则冲突等比如参数校验不通过、权限校验失败。抛出异常好处终止业务逻辑的继续执行。
-
非业务异常表示不在预期内的问题通常由类库、框架抛出或由于自己的代码逻辑错误导致比如数据库连接失败、空指针异常、除0错误等等。
技术细节实现上所有异常返回给用户时都需要两类信息
-
异常对应的HTTP响应码
-
异常的文本描述信息
Spring MVC 下具体技术解决方案在Controller 层使用统一的异常拦截器
-
设置 HTTP 响应的状态码业务类异常使用它指定的 HTTP code非业务类异常统一使用500
-
Response Body 的错误码异常类名
-
Response Body 的错误描述业务类异常使用它指定的错误文本
8. 其他
常用命名方法
-
DAO 层方法命名
insert
、update
、getBy*
、delete
-
Service 层方法的命名
add
、update*
、getBy*
、remove
-
Controller 层方法命名
create
、update
、getBy*
、实际操作的语意on
、off
、publish
、delete
、sync
出错信息
1. `// 错误信息`
2. `{`
3. `"success": false,`
4. `"data" : {`
5. `"reason" : "没有权限",`
6. `"message": "详细信息..."`
7. `}`
8. `}`
PUT vs. POST:
-
POSTAjax 中对 post 方法提供了封装能够快捷调用http://stackoverflow.com/q/2153917
-
PUT通常要求更新操作具备幂等性