1.不用大写;
2.用中杠-
不用下杠_
;
3.参数列表要encode;
4.URI中的名词表示资源集合,使用复数形式。
在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。
举例来说,有一个API提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样。
接口尽量使用名词,禁止使用动词,下面是一些例子。
GET /zoos:列出所有动物园 POST /zoos:新建一个动物园 GET /zoos/ID:获取某个指定动物园的信息 PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息) PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息) DELETE /zoos/ID:删除某个动物园 GET /zoos/ID/animals:列出某个指定动物园的所有动物 DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
反例:
/getAllCars /createNewCar /deleteAllRedCars
过深的导航容易导致url膨胀,不易维护,如 GET /zoos/1/areas/3/animals/4
,尽量使用查询参数代替路径中的实体导航,如
通过标准HTTP方法对资源CRUD:
GET:查询
GET /zoos GET /zoos/1 GET /zoos/1/employees
POST /animals //新增动物 POST /zoos/1/employees //为id为1的动物园雇佣员工
PUT /animals/1
PUT /zoos/1
DELETE:删除
DELETE /zoos/1/employees/2 DELETE /zoos/1/employees/2;4;5 DELETE /zoos/1/animals //删除id为1的动物园内的所有动物
HEAD / OPTION 用的不多,就不多解释了。
幂等性:执行1次和执行N次,对资源状态改变的效果是等价的。
安全性 | 幂等性 | |
---|---|---|
GET | √ | √ |
POST | × | × |
PUT | × | √ |
DELETE | × | √ |
安全性和幂等性均不保证反复请求能拿到相同的response。以 DELETE 为例,第一次DELETE返回200表示删除成功,第二次返回404提示资源不存在,这是允许的。
示例 | 备注 | |
---|---|---|
过滤条件 | ?type=1&age=16 |
允许一定的uri冗余,如/zoos/1 与/zoos?id=1 |
排序 | ?sort=age,desc |
|
投影 | ?whitelist=id,name,email |
|
分页 | ?limit=10&offset=3 |
如:
GET /trades?status=closed&sort=created,desc
快捷方式:
GET /trades#recently-closed
或者
GET /trades/recently-closed
只用以下常见的3种body format:
Content-Type: application/json
POST /v1/animal HTTP/1.1 Host: api.example.org Accept: application/json Content-Type: application/json Content-Length: 24 { "name": "Gir", "animalType": "12" }
POST /login HTTP/1.1 Host: example.com Content-Length: 31 Accept: text/html Content-Type: application/x-www-form-urlencoded username=root&password=Zion0101
HTTP/1.1 200 OK { "code": 200, "message": "处理成功", "success": true, "data": { } }
1.大家使用同一的Result返回列,不要每个模块使用自己内部的返回类,避免类名冲突导致误用;
2.Feign接口不允许返回枚举,返回枚举可能因枚举变更导致序列化问题;
3.外部接口不能返回内部字段,如createTime,createBy,updateTime,updateTIme
1.不要发生了错误但给2xx响应,客户端可能会缓存成功的http请求;
2.正确设置http状态码,不要自定义;
3.Response body 提供 1) 错误的代码(日志/问题追查);2) 错误的描述文本(展示给用户)。
4.聚合接口不能将内部异常(比如接口熔断)抛给客户端或前端,需要将内部异常转成用户语言返回;
5.Feign接口不允许直接抛出异常,异常信息通过ResultCode和ResultMessage返回;
对第三点的实现稍微多说一点:
Java 服务器端一般用异常表示 RESTful API 的错误。API 可能抛出两类异常:业务异常和非业务异常。业务异常
1.如果抛出该类异常,HTTP 响应状态码应该设成什么;
2.异常的文本描述;
在Controller层使用统一的异常拦截器:
1.设置 HTTP 响应状态码:对业务类异常,用它指定的 HTTP code;对非业务类异常,统一500;
2.Response Body 的错误码:异常类名
3.Response Body 的错误描述:对业务类异常,用它指定的错误文本;对非业务类异常,线上可以统一文案如“服务器端错误,请稍后再试”,开发或测试环境中用异常的 stacktrace,服务器端提供该行为的开关。
常用的http状态码及使用场景:
使用场景 | |
---|---|
400 bad request | 常用在参数校验 |
401 unauthorized | 未经验证的用户,常见于未登录。如果经过验证后依然没权限,应该 403(即 authentication 和 authorization 的区别)。 |
403 forbidden | 无权限 |
404 not found | 资源不存在 |
500 internal server error | 非业务类异常 |
503 service unavaliable | 由容器抛出,自己的代码不要抛这个异常 |
除了资源简单的CRUD,服务器端经常还会提供其他服务,这些服务无法直接用上面提到的URI映射。如:
1.按关键字搜索;
2.计算地球上两点间的距离;
3.批量向用户推送消息
可以把这些服务看成资源,计算的结果是资源的presentation,按服务属性选择合适的HTTP方法。
例:
GET /search?q=filter?category=file 搜索 GET /distance-calc?lats=47.480&lngs=-122.389&late=37.108&lnge=-122.448 POST /batch-publish-msg[{"from":0,"to":1,"text":"abc"},{},{}...]】
提交任务: POST /batch-publish-msg [{"from":0,"to":1,"text":"abc"},{},{}...] 返回: {"taskId":3,"createBy":"Anonymous","status":"running"} GET /task/3 {"taskId":3,"createBy":"Anonymous","status":"success"}
提交任务: POST /batch-publish-msg [{"from":0,"to":1,"text":"abc"},{},{}...] 返回: {"taskId":3,"createBy":"Anonymous"} GET /task/3/status {"progress":"50%","total":18,"success":8,"fail":1}
版本
常见的三种方式:
1.在uri中放版本信息:GET /v1/users/1
2.Accept Header:Accept: application/json+v1
3.自定义 Header:X-Api-Version: 1
接口定义jar包在更新后需要升级pom版本,线上不允许使用snapshot版本;
URI失效
2、Feign接口不允许直接修改已有接口的定义(包括入参和返回值),如需修改,需要新增接口,如老接口后续会废弃,
则使用@deprecated进行注释;废弃时需要给出具体的deadlin,并知会到各个使用方;