REST全称是Representational State Transfer
,中文意思是表征性状态转移
它首次出现在2000年Roy Fielding的博士论文中,Roy Fielding是HTTP规范的主要编写者之一
他在论文中提到:"我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构
REST指的是一组架构约束条件和原则
。“如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。”
REST本身并没有创造新的技术、组件或服务,而隐藏在RESTful背后的理念就是使用Web的现有特征和能力, 更好地使用现有Web标准中的一些准则和约束
RESTful 是目前最流行的 API 设计规范,用于 Web 数据接口的设计
首先REST指的是表征性状态转移,而这里的表征性实际指的就是网络当中的资源
任何事物,只要有被引用到的必要,它就是一个资源:
在Web中我们使用统一资源定位符URI(Uniform Resource Identifier)
定义一个资源,以此识别一个资源
URI既可以看成是资源的地址,也可以看成是资源的名称
为了让URI更加清晰、直观、明了,URI的设计应该遵循可寻址性
原则,具有自描述性
,需要在形式上给人以直觉上的关联
因此定义RESTful API,主要就是规范软件架构风格、设计风格,使其遵循一定的设计原则和约束条件,使得基于RESTful风格设计接口可以更简洁,更有层次,更易于实现缓存等机制。
尽量将API部署在专用域名之下
https://api.example.com
如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下
https://example.org/api/
将API的版本号放入URL
http://www.example.com/app/1.0/foo
http://www.example.com/app/1.1/foo
版本号也可以在HTTP请求头信息的Accept字段中进行区分
Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
RESTful 的核心思想就是,客户端发出的数据操作指令都是"动词 + 宾语"
的结构。比如,GET /papers
这个命令,GET
是动词,/papers
是宾语
也就是说在设计URL是需要遵循"动词 + 宾语"
的结构
HTTP动词是对资源的具体操作类型, 就是所谓的请求方式
常用的HTTP动词有下面四个(括号里是对应的SQL命令)
GET(SELECT):从服务器取出资源(一项或多项)
POST(CREATE):在服务器新建一个资源
PUT(UPDATE):在服务器更新资源(全部更新)
DELETE(DELETE):从服务器删除资源
# 其他动词
PATCH(UPDATE):在服务器更新资源(部分更新)
HEAD:获取资源的元数据
OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的
动词补充:
有些客户端只能使用GET
和POST
这两种方法。服务器必须接受POST
模拟其他三个方法(PUT、PATCH、DELETE)
这时,客户端发出的 HTTP 请求,要加上X-HTTP-Method-Override
属性,告诉服务器应该使用哪一个动词,覆盖POST
方法
POST /api/Person/4 HTTP/1.1
X-HTTP-Method-Override: PUT # X-HTTP-Method-Override指定本次请求的方法是PUT,而不是POST
宾语就是接口具体的 URL,是 HTTP 动词作用的对象
名词
,不能有动词
,而且所用的名词往往与数据库的表名对应/papers
这个 URL 就是正确的,而下面的 URL 不是名词,所以都是错误的/getPapers
/listCarts
/retreiveAllBooks
# 获取单个产品:
http://127.0.0.1:8888/papers/1
# 获取所有产品:
http://127.0.0.1:8888/papers
基于上面提到的动词+宾语
设计原则,这里给出几个常见的范例
GET /librarys # 列出所有图书馆
POST /librarys # 新建一个图书馆(上传文件)
GET /librarys/ID # 获取某个指定图书馆的信息
PUT /librarys/ID # 更新某个指定图书馆的信息(提供该图书馆的全部信息)
PATCH /librarys/ID # 更新某个指定图书馆的信息(提供该图书馆的部分信息)
DELETE /librarys/ID # 删除某个图书馆
GET /librarys/ID/books # 列出某个指定图书馆的所有书籍
DELETE /librarys/ID/books/ID # 删除某个指定图书馆的指定书籍
即当记录的数量很多时 , 通常需要利用资源的一些属性对结果进行过滤
如果将资源的属性作为URL的宾语往往不利于扩展,通常使用查询字符串
表示过滤信息
常见参数:
?limit=20 # 指定返回记录的数量
?offset=20 # 指定返回记录的开始位置
?page=2&per_page=100 # 指定第几页,以及每页的记录数,多个条件使用“&”拼接
?book_type=historical # 指定书本分类条件
状态码是HTTP协议内容的一部分,它代表了服务器响应客户端请求的结果,不同的状态码对应服务器不同的响应状态
我们当然可以随心所欲的让服务器返回状态码,但是这往往会给客户端带来困扰
因此符合RESTful的状态码设计应该能够准确反映服务器的响应状态
HTTP 状态码就是一个三位数,分成五个类别:
1xx:相关信息
2xx:操作成功
3xx:重定向
4xx:客户端错误
5xx:服务器错误
这五大类总共包含100多种状态码,覆盖了绝大部分可能遇到的情况
每一种状态码都有标准的(或者约定的)解释,客户端只需查看状态码,就可以判断出发生了什么情况,所以服务器应该返回尽可能精确的状态码
其中1xx通常用于要求客户端继续发送更多请求相关的数据或信息,在RESTful API中基本涉及不到
200状态码表示操作成功,但是不同HTTP请求的方法可以返回更精确的状态码
GET: 200 OK # 服务器成功返回用户请求的数据
POST/PUT/PATCH: 201 CREATED # 用户新建或修改数据成功
ALL Type: 202 Accepted # 表示一个请求已经进入后台排队(异步任务)
DELETE: 204 No Content # 用户删除数据成功
其中202异步任务返回结果的例子
HTTP/1.1 202 Accepted
{
"task": {
"href": "/api/company/job-management/jobs/2130040",
"id": "2130040"
}
}
返回结果
当请求成功时,对于不同的HTTP请求,服务器也应该返回不同的结果
GET /librarys # 请求所有资源,返回资源对象的列表
GET /librarys/ID # 请求单个资源,返回单个资源对象
POST /librarys # 返回新生成的资源对象
PUT /librarys/ID # 更新单个资源,返回完整的单个资源对象
PATCH /librarys/ID # 部分更新单个资源,返回完整的单个资源对象
DELETE /librarys/ID # 删除单个对象,返回一个空文档
API 用不到301
状态码(永久重定向)和302
状态码(暂时重定向,307
也是这个含义),因为它们可以由应用级别返回,浏览器会直接跳转,API 级别可以不考虑这两种情况
API 用到的3xx状态码,主要是303 See Other
,表示参考另一个 URL
它与302
和307
的含义一样,也是"暂时重定向",区别在于302
和307
用于GET
请求,而303
用于POST
、PUT
和DELETE
请求
收到303
以后,浏览器不会自动跳转,而会让用户自己决定下一步怎么办。下面是一个例子:
HTTP/1.1 303 See Other
Location: /api/orders/12345
301: 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302: 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
4xx状态码表示客户端错误,主要有下面几种
400 INVALID REQUEST # POST/PUT/PATCH:用户发出的请求有错误,服务器没有进行新建或修改数据的操作
401 Unauthorized # All Type:表示用户没有权限(令牌、用户名、密码错误)
403 Forbidden # All Type:表示用户得到授权,但是权限不足导致访问被禁止
404 NOT FOUND # All Type:用户发出的请求针对的是不存在的记录
406 Not Acceptable # GET:服务器无法根据客户端请求的内容特性完成请求
410 Gone # GET:所请求的资源已从这个地址转移,不再可用
415 Unsupported Media Type # 客户端要求的返回格式不支持。比如,API 只能返回 JSON 格式,但是客户端要求返回 XML 格式
422 Unprocesable entity # POST/PUT/PATCH:客户端上传的附件无法处理,导致请求失败
429 Too Many Requests # 客户端的请求次数超过限额
5xx 状态码表示服务端错误。一般来说,API 不会向用户透露服务器的详细信息,所以只要两个状态码就够了
500 Internal Server Error # 客户端请求有效,服务器处理时发生了意外
503 Service Unavailable # 服务器无法处理请求,一般用于网站维护状态
API 返回的数据格式,不应该是纯文本,而应该是一个 JSON 对象,因为这样才能返回标准的结构化数据
所以,服务器回应的 HTTP 头的Content-Type
属性要设为application/json
客户端请求时,也要明确告诉服务器,可以接受JSON
格式,即请求的 HTTP 头的ACCEPT
属性也要设成application/json
响应头示例
GET /orders/2 HTTP/1.1
Accept: application/json
RESTful API最好做到Hypermedia
(即返回结果中提供链接,指向其他API)
这样的话,用户只要记住一个 URL,不用查看接口文档,就可以发现其他的 URL
例如https://api.github.com/
{
"current_user_url": "https://api.github.com/user",
"current_user_authorizations_html_url": "https://github.com/settings/connections/applications{/client_id}",
"authorizations_url": "https://api.github.com/authorizations",
"code_search_url": "https://api.github.com/search/code?q={query}{&page,per_page,sort,order}",
"commit_search_url": "https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}",
"emails_url": "https://api.github.com/user/emails",
// ...
}
参考资料:
http://www.ruanyifeng.com/blog/2018/10/restful-api-best-practices.html