URI规范
- 统一使用小写字母
- 使用字符"-"代替下划线"_"的使用
- 参数列表要encode
- URI中的名词表示资源集合,使用复数形式。
- URI中有些字段可以是变量,在实际使用中可以按需替换
- 避免层级过深的URI /在url中表达层级,用于按实体关联关系进行对象导航,一般根据id导航。 过深的导航容易导致url膨胀,不易维护,如 GET /zoos/1/areas/3/animals/4,尽量使用查询参数代替路径中的实体导航,如GET /animals?zoo=1&area=3;
HTTP 动词
- GET (查询):从服务器上获取一个具体的资源或者一个资源列表。
GET /orders 查询所有订单
GET /orders/2016112417581511507 查询订单号为2016112417581511507的订单
GET /teams/1/developers 查询1号组所有开发人员
- POST (创建): 在服务器上创建一个新的资源。
POST /orders //新增订单
POST /teams/1/developers //id为1的组增加开发人员
- PUT (更新):以整体的方式更新服务器上的一个资源。
PUT /orders/2016112417581511507 更新订单2016112417581511507所有信息
PUT /teams/1/developers/3146 更新1号组内开发人员3146所有信息
- PATCH (更新):只更新服务器上一个资源的一个属性。
PATCH /orders/2016112417581511507 更新订单2016112417581511507部分信息
PATCH /teams/1/developers/3146 更新1号组内开发人员3146部分信息
- DELETE (删除):删除服务器上的一个资源。
DELETE /orders/2016112417581511507 删除订单2016112417581511507
DELETE /teams/1/developers/3146 删除1号组内开发人员3146所有信息
- HEAD : 获取一个资源的元数据,如数据的哈希值或最后的更新时间。
- OPTIONS:获取客户端能对资源做什么操作的信息。
安全性和幂等性
- 安全性:不会改变资源状态,可以理解为只读的;
- 幂等性:执行1次和执行N次,对资源状态改变的效果是等价的。
方法 | 安全性 | 幂等性 |
---|---|---|
GET | √ | √ |
POST | × | × |
PUT | × | √ |
DELETE | × | √ |
资源的原型
- 文档(Document)类型的资源用名词(短语)单数命名
文档是资源的单一表现形式,可以理解为一个对象,或者数据库中的一条记录。在请求文档时,要么返回文档对应的数据,要么会返回一个指向另外一个资源(文档)的链接。例如:
- GET /teams/1/staffs/239355111 1号组下编号为239355111的员工
- GET /orders/2016112417581511507 订单号为2016112417581511507的订单
- 集合(Collection)类型的资源用名词(短语)复数命名
集合可以理解为是资源的一个容器(目录),我们可以向里面添加资源(文档)。例如:
- POST /teams/1/staffs 1号组增加新员工
- GET /users/239355111/orders 查询用户239355111下所有订单列表
- 仓库(Store)类型的资源用名词(短语)复数命名
- 控制器(Controller)类型的资源用动词(短语)命名
控制器资源模型,可以执行一个方法,支持参数输入,结果返回。 是为了除了标准操作:增删改查(CRUD)以外的一些逻辑操作。控制器(方法)一般定义子URI中末尾,并且不会有子资源。例如:
- POST /messages/12345/resend 向用户重发ID为245743的消息
复杂查询
- 过滤条件 ?type=1&age=16 允许一定的uri冗余,如/zoos/1与/zoos?id=1
- 排序 ?sort=age,desc
- 投影 ?whitelist=id,name,email
- 分页 ?limit=10&offset=3
响应数据
不要包装:response 的 body 直接就是数据,不要做多余的包装。
错误示例:
{
"success":true,
"data":{"id":1,"name":"xiaotuan"},
}
正确示例:
{"id":1,"name":"xiaotuan"}
各HTTP方法成功处理后的数据格式:
- GET: 单个对象、集合
- POST: 新增成功的对象
- PUT/PATCH: 更新成功的对象
- DELETE: 空
json格式的约定:
- 时间用长整形(毫秒数),客户端自己按需解析(moment.js)
- 不传null字段
HTTP响应状态码
- 200 (“OK”) 用于一般性的成功返回
- 201 (“Created”) 资源被创建
- 202 (“Accepted”) 用于Controller控制类资源异步处理的返回,仅表示请求已经收到。对于耗时比较久的处理,一般用异步处理来完成
- 204 (“No Content”) 此状态可能会出现在PUT、POST、DELETE的请求中,一般表示资源存在,但消息体中不会返回任何资源相关的状态或信息。
- 301 (“Moved Permanently”) 资源的URI被转移,需要使用新的URI访问
- 302 (“Found”) 不推荐使用,此代码在HTTP1.1协议中被303/307替代。我们目前对302的使用和最初HTTP1.0定义的语意是有出入的,应该只有在GET/HEAD方法下,客户端才能根据Location执行自动跳转,而我们目前的客户端基本上是不会判断原请求方法的,无条件的执行临时重定向
- 303 (“See Other”) 返回一个资源地址URI的引用,但不强制要求客户端获取该地址的状态(访问该地址)
- 304 (“Not Modified”) 有一些类似于204状态,服务器端的资源与客户端最近访问的资源版本一致,并无修改,不返回资源消息体。可以用来降低服务端的压力
- 307 (“Temporary Redirect”) 目前URI不能提供当前请求的服务,临时性重定向到另外一个URI。在HTTP1.1中307是用来替代早期HTTP1.0中使用不当的302
- 400 (“Bad Request”) 用于客户端一般性错误返回, 在其它4xx错误以外的错误,也可以使用400,具体错误信息可以放在body中
- 401 (“Unauthorized”) 在访问一个需要验证的资源时,验证错误
- 403 (“Forbidden”) 一般用于非验证性资源访问被禁止,例如对于某些客户端只开放部分API的访问权限,而另外一些API可能无法访问时,可以给予403状态
- 404 (“Not Found”) 找不到URI对应的资源
- 405 (“Method Not Allowed”) HTTP的方法不支持,例如某些只读资源,可能不支持POST/DELETE。但405的响应header中必须声明该URI所支持的方法
- 406 (“Not Acceptable”) 客户端所请求的资源数据格式类型不被支持,例如客户端请求数据格式为application/xml,但服务器端只支持application/json
- 409 (“Conflict”) 资源状态冲突,例如客户端尝试删除一个非空的Store资源
- 412 (“Precondition Failed”) 用于有条件的操作不被满足时
- 415 (“Unsupported Media Type”) 客户所支持的数据类型,服务端无法满足
- 500 (“Internal Server Error”) 服务器端的接口错误,此错误于客户端无关
1xx范围的状态码是保留给底层HTTP功能使用的,并且估计在你的职业生涯里面也用不着手动发送这样一个状态码出来。
2xx范围的状态码是保留给成功消息使用的,你尽可能的确保服务器总发送这些状态码给用户。
3xx范围的状态码是保留给重定向用的。大多数的API不会太常使用这类状态码,但是在新的超媒体样式的API中会使用更多一些。
4xx范围的状态码是保留给客户端错误用的。例如,客户端提供了一些错误的数据或请求了不存在的内容。这些请求应该是幂等的,不会改变任何服务器的状态。
5xx范围的状态码是保留给服务器端错误用的。这些错误常常是从底层的函数抛出来的,并且开发人员也通常没法处理。发送这类状态码的目的是确保客户端能得到一些响应。收到5xx响应后,客户端没办法知道服务器端的状态,所以这类状态码是要尽可能的避免。
错误处理
- 不要发生了错误但给2xx响应,客户端可能会缓存成功的http请求;
- 正确设置http状态码,不要自定义;
- Response body 提供 1) 错误的代码(日志/问题追查);2) 错误的描述文本(展示给用户)。
使用详细的错误包装错误:
{
"errors":
[
{
"userMessage": "Sorry, the requested resource does not exist",
"internalMessage": "No car found in the database",
"code": 34,
"more info": "http://dev.mwaysolutions.com/blog/api/v1/errors/12345"
}
]
}
常用的http状态码使用场景:
- 400 bad request 常用在参数校验
- 401 unauthorized 未经验证的用户,常见于未登录。如果经过验证后依然没权限,应该 403(即 authentication 和 authorization 的区别)。
- 403 forbidden 无权限
- 404 not found 资源不存在
- 500 internal server error 非业务类异常
- 503 service unavaliable 由容器抛出,自己的代码不要抛这个异常
API的演进版本
三种方式:
- 在uri中放版本信息:GET /v1/users/1 最为常用
- Accept Header:Accept: application/json+v1
- 自定义 Header:X-Api-Version: 1
使用Http头声明序列化格式
在客户端和服务端,双方都要知道通讯的格式,格式在HTTP-Header中指定 Content-Type 定义请求格式 如:Content-Type: application/json Accept 定义系列可接受的响应格式 如: Accept: application/json
HTTP Request
POST /v1/animal HTTP/1.1
Host: api.example.org
Accept: application/json
Content-Type: application/json
Content-Length: 24
{
"name": "Gir",
"type": 12
}
HTTP Response
HTTP/1.1 200 OK
Date: Wed, 18 Dec 2013 06:08:22 GMT
Content-Type: application/json
Access-Control-Max-Age: 1728000
Cache-Control: no-cache
{
"id": 12,
"created": 1386363036,
"modified": 1386363036,
"name": "Gir"
}