上来一首打油诗:
动词加名词,名词用复数。
动词分五类,之间可覆盖。
名词若多级,从属或平等。
状态需明确,响应给连接。
1. 什么叫做Restful
简单来讲:Restful是一种框架的规范与约束原则,符合这种规范的架构都是RESTful架构
1.1 RESTful是什么意思?
REST是Representational State transfer (资源)表述性转移。本质上是通过表述完成资源的转移。
1.2 RESTful的结构?
RESTful的核心思想就是:客户端发出的数据操作指令都是“动词+宾语”的结构。例如GET /articles
这个命令,GET
是动词,/articles
是宾语。
需要注意的是:根据HTTP规范,动词一律大写。
动词通常来说,其实就是五种HTTP方法,对应CRUD操作。
动词 | 操作 |
---|---|
GET | 读取 |
POST | 新建 |
PUT | 更新 |
PATCH | 部分更新 |
DELETE | 删除 |
作为设计基础,还是需要遵循几个原则的:
- 当标准合理的时候遵守标准。
- API应该对程序员友好,并且在浏览器地址容易输入。
- API应该简单,直观,容易使用的同时优雅。
- API应该具有足够的灵活性来支持上层ui。
- API设计权衡上述几个原则。
- GET
/tickets
#获取ticket列表; - GET
/tickets/12
#查看某个具体的ticket; - POST
/tickets
#新建一个ticket; - PUT
/tickets/12
#更新ticket 12; - DELETE
/tickets/12
#删除ticket 12;
显然从API用户的角度来看,“资源”应该是一个名词。即使在内存数据结构模型和资源已经有了很好的对应,API设计的时候仍然不需要把它们一对一的暴露出来,这里的关键是隐藏内部资源,暴露必须的对外资源。
2. 如何书写RESTful
2.1 动词的覆盖
有些客户端只能使用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。
2.1 宾语必须是名词
宾语就是API的URL,是Http动词作用的对象,他表述的是一个资源。应该是名词,不应该是动词。
!!!Restful反例
/getAllCars
/createNewCar
/deleteAllRedCars
2.2 宾语是单数还是复数
既然URL是名词,那么应该使用复数,还是单数?
这里没有统一的规定,但是常见的操作是读取一个集合,比如GET /articles
(读取所有的文章)这里明显是复数。
为了统一起见,建议都使用复数URL,比如GET /articles/2
要好于GET /article/2
。
2.3 多级的URL的写法
按照Rails中标准定义的方式:
- 若定义这个资源的两个参数具有从属关系,那么生成的url就是
owner/1/property/1
,其中property从属于owner。比如用户1的编号为2的文章,就是users/1/articles/2
。 - 如果两个资源是平等的,那么一般是作为URL中HTTP方法的参数。
举个小栗子
比如汇款动作,从账户1向账户2汇款500元。如何设计URL?
小优同学:POST /accounts/1/transfer/500/to/2
;
小胖老师:答错了,请坐下。
正确的应该是:将动词转换为名称,(只是一个汇款操作)
即:POST /accounts/transaction?from=1&to=2&amount=500.00
;
3. 状态码
客户端的每一次请求,服务器都必须给出回应。回应包括HTTP状态码和数据两部分。
状态码 | 相关操作 |
---|---|
1xx | 相关信息 |
2xx | 操作成功 |
3xx | 重定向 |
4xx | 客户端错误 |
5xx | 服务器错误 |
这五大类共包含了100多种状态码,覆盖了绝大部分可能的情况。每一种状态码都有标准(或者是约定)解释,客户端只需要查看状态码,就可以判断出发生了什么情况,所以服务器应返回尽可能精确的1状态码。
API不需要1xx状态码,下面介绍其他四类状态码的精确含义。
3.1 2xx状态码
虽然
200
状态码表示操作成功,但是不同的方法可以返回更精确的状态码。
HTTP操作 | 状态码 |
---|---|
GET | 200 ok |
POST | 201 created |
PUT | 200 ok |
PATCH | 200 ok |
DELETE | 204 no content |
-
POST返回
201
状态码,表示生成了新的资源; -
DELETE返回
204
状态码,表示资源已经不存在; -
202 Accepted
状态码表示服务器已经收到请求,但还未进行处理,会在未来再处理,通常用于异步操作。
HTTP/1.1 202 Accepted
{
"task": {
"href": "/api/company/job-management/jobs/2130040",
"id": "2130040"
}
}
3.2 3xx状态码
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
3.3 4xx状态码
4xx状态码表示客户端错误,主要有下面几种。
状态码 | 含义 |
---|---|
400 Bad Request | 服务器不理解客户端请求,未做任何处理 |
401 Unauthorized | 用户未提供身份验证凭证,或者没有通过身份验证 |
403 Forbidden | 用户通过身份验证,但没有权限访问请求资源 |
404 Not Found | 所请求资源不存在,或不可用 |
405 Method Not Allowed | 用户已经通过身份验证,但是所用HTTP方法不在权限内 |
410 Gone | 所请求资源已经从这个地址转移,不可再用 |
415 Unsupported Media Type | 客户端要求返回格式不支持。比如API只能返回JSON,但Client要求返回XML |
422 Unprocessable Entity | 客户端上传附件无法处理,导致请求失败 |
429 Too Many Request | 客户端的请求次数超过限额 |
3.4 5xx状态码
5xx状态码表示服务端错误,一般来说,API不会像用户透露服务器的详细信息,所以只要两个状态码就够了。
状态码 | 含义 |
---|---|
500 Internal Server Error | 客户端请求有效,服务器处理时发生了意外 |
503 Service Unavailable | 客户端无法处理请求,一般用于网站维护状态 |
4. 服务器回应
4.1 不要返回纯文本
API返回的数据格式,不应该是纯文本,而应该是一个JSON对象,因为这样才能返回标准的结构化数据。所以客户端回应的HTTP头的Content-Type属性要设为
application/json
。
客户端请求时,也要明确告诉服务器,可以接受JSON格式,即请求的HTTP头的ACCEPT属性也要设为application/json
。
GET /orders/2 HTTP/1.1
Accept: application/json
4.2 发生错误时,不要返回200错误码
有一种不恰当的做法是,即使发生错误,也返回200状态码,把错误信息放在数据体里面,就像下面这种:
HTTP/1.1 200 OK
Content-Type: application/json
{
"status": "failure",
"data": {
"error": "Expected at least two items in list."
}
}
上面代码,解析数据体以后,才能得知操作失败。
这种做法实际上是取消了状态码,这是完全不可取的。正确的做法是:状态码反映发生的错误,具体的错误信息放在数据体里面返回。
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "Invalid payoad.",
"detail": {
"surname": "This field is required."
}
}
4.3 提供链接
HATEOAS怎么读(Hypermedia as the engine of application state)超媒体作为应用状态引擎。【“小胖哥哥,你英语翻译太棒了”——小优】
我们知道REST是使用标准的HTTP方法来操作资源的。但仅仅因此就理解成带CURD的WEB数据库架构就太low了吧。这种说法忽略了一个核心的概念,即“超媒体即应用状态引擎(Hypermedia as the engine of application state)
”。
超媒体是什么?
当你浏览Web页面的时候,从一个连接跳到一个页面,再从另一个连接跳到另外一个页面,就是利用超媒体的概念:把一个个资源链接起来。
要达到这个目的,就要求在表述表格里面加入链接来引导客户端,在《RESTFul Web Services》一书中,作者把这种具有链接的特性称为“连通性”。
RESTful API最好做到HATEOAS,即返回结果中提供链接,连向其他API方法,使得用户不用查询文档,也知道下一步怎么做。比如,用户向api.example.com
的根路径发出请求,会得到这样一个文档。
{"link": {
"rel": "collection https://www.example.com/zoos",
"href": "https://api.example.com/zoos",
"title": "List of zoos",
"type": "application/vnd.yourformat+json"
}}
上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步调用什么API,rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。
Hypermedia API的设计被称为HATEOAS。GIThub的API就是这种设计,访问api.github.com就会得到一个所有可用API的网址列表。
HATEOAS实践那我们就去访问访问https://api.github.com/user这个地址吧。
访问api.github.com/user参考文章:
restful 接口命名规则
网友评论