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 头部的返回码统一用200(OK),具体状态在 body 中使用参数标识
-
body 中返回码的值,与 HTTP 协议中返回码的值保持一致,便于大家统一理解
HTTP 常见状态码:
-
操作成功,2xx:200,ok;201,created
-
Client Error,4xx:400,bad request;401,未授权;404,not found
-
Server Error,5xx:500,internal server error
小结
REST,Representational States Transfer,表现层状态转化 & 有状态传输,本质上:REST 关注于资源,将所有的内容看做一个资源:图片、文本、计算,为每一个资源分配唯一的地址,并对这些资源进行规范的操作。
几个方面:
-
资源的地址:URI,URL(URI 是抽象,URL 是具体实现)
-
资源的形式:实体文件如:图片,文本,html,json。甚至一些算法及服务
-
对资源的操作: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是title,message是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:
-
POST:Ajax 中,对 post 方法提供了封装,能够快捷调用,http://stackoverflow.com/q/2153917
-
PUT:通常要求更新操作具备幂等性