写在前面
REST,即Representational State Transfer的缩写。直接翻译的意思是”表现层状态转化”。
它是一种互联网应用程序的API设计理念:URL定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操作。
近年来移动互联网的发展,前端设备层出不穷(手机、平板、桌面电脑、其他专用设备……),因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信,于是RESTful诞生了,它可以通过一套统一的接口为 Web,iOS和Android提供服务。
URI
即统一资源标识符,服务器上每一种资源,比如文档、图像、视频片段、程序 都由一个通用资源标识符(Uniform Resource Identifier, 简称”URI”)进行定位。
HTTP动词
常用的HTTP动词有下面五个
- GET(SELECT):从服务器取出资源(一项或多项)。
- POST(CREATE):在服务器新建一个资源。
- PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
- PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
- DELETE(DELETE):从服务器删除资源。
RESTful架构
服务器上每一种资源,比如一个文件,一张图片,一部电影,都有对应的url地址,如果我们的客户端需要对服务器上的这个资源进行操作,就需要通过http协议执行相应的动作来操作它,比如进行获取,更新,删除。
简单来说就是url地址中只包含名词表示资源,使用http动词表示动作进行操作资源
举个例子:左边是错误的设计,而右边是正确的
GET /blog/getArticles --> GET /blog/Articles 获取所有文章
GET /blog/addArticles --> POST /blog/Articles 添加一篇文章
GET /blog/editArticles --> PUT /blog/Articles 修改一篇文章
GET /rest/api/deleteArticles?id=1 --> DELETE /blog/Articles/1 删除一篇文章
统一资源接口
RESTful架构应该遵循统一接口原则,统一接口包含了一组受限的预定义的操作,不论什么样的资源,都是通过使用相同的接口进行资源的访问。接口应该使用标准的HTTP方法如GET,PUT和POST,并遵循这些方法的语义。
如果按照HTTP方法的语义来暴露资源,那么接口将会拥有安全性和幂等性的特性,例如GET和HEAD请求都是安全的, 无论请求多少次,都不会改变服务器状态。而GET、HEAD、PUT和DELETE请求都是幂等的,无论对资源操作多少次, 结果总是一样的,后面的请求并不会产生比第一次更多的影响。
下面列出了GET,DELETE,PUT和POST的典型用法:
GET
- 安全且幂等
- 获取表示
- 变更时获取表示(缓存)
- 200(OK) - 表示已在响应中发出
- 204(无内容) - 资源有空表示
- 301(Moved Permanently) - 资源的URI已被更新
- 303(See Other) - 其他(如,负载均衡)
- 304(not modified)- 资源未更改(缓存)
- 400 (bad request)- 指代坏请求(如,参数错误)
- 404 (not found)- 资源不存在
- 406 (not acceptable)- 服务端不支持所需表示
- 500 (internal server error)- 通用错误响应
- 503 (Service Unavailable)- 服务端当前无法处理请求
POST
- 不安全且不幂等
- 使用服务端管理的(自动产生)的实例号创建资源
- 创建子资源
- 部分更新资源
- 如果没有被修改,则不过更新资源(乐观锁)
- 200(OK)- 如果现有资源已被更改
- 201(created)- 如果新资源被创建
- 202(accepted)- 已接受处理请求但尚未完成(异步处理)
- 301(Moved Permanently)- 资源的URI被更新
- 303(See Other)- 其他(如,负载均衡)
- 400(bad request)- 指代坏请求
- 404 (not found)- 资源不存在
- 406 (not acceptable)- 服务端不支持所需表示
- 409 (conflict)- 通用冲突
- 412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
- 415 (unsupported media type)- 接受到的表示不受支持
- 500 (internal server error)- 通用错误响应
- 503 (Service Unavailable)- 服务当前无法处理请求
PUT
- 不安全但幂等
- 用客户端管理的实例号创建一个资源
- 通过替换的方式更新资源
- 如果未被修改,则更新资源(乐观锁)
- 200 (OK)- 如果已存在资源被更改
- 201 (created)- 如果新资源被创建
- 301(Moved Permanently)- 资源的URI已更改
- 303 (See Other)- 其他(如,负载均衡)
- 400 (bad request)- 指代坏请求
- 404 (not found)- 资源不存在
- 406 (not acceptable)- 服务端不支持所需表示
- 409 (conflict)- 通用冲突
- 412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
- 415 (unsupported media type)- 接受到的表示不受支持
- 500 (internal server error)- 通用错误响应
- 503 (Service Unavailable)- 服务当前无法处理请求
DELETE
- 不安全但幂等
- 删除资源
- 200 (OK)- 资源已被删除
- 301 (Moved Permanently)- 资源的URI已更改
- 303 (See Other)- 其他,如负载均衡
- 400 (bad request)- 指代坏请求
- 404 (not found)- 资源不存在
- 409 (conflict)- 通用冲突
- 500 (internal server error)- 通用错误响应
- 503 (Service Unavailable)- 服务端当前无法处理请求
POST和PUT用于创建资源时有什么区别?
POST和PUT在创建资源的区别在于,所创建的资源的名称(URI)是否由客户端决定。 例如为我的博文增加一个java的分类,生成的路径就是分类名/categories/java,那么就可以采用PUT方法。不过很多人直接把POST、GET、PUT、DELETE直接对应上CRUD,例如在一个典型的rails实现的RESTful应用中就是这么做的。
我认为,这是因为rails默认使用服务端生成的ID作为URI的缘故,而不少人就是通过rails实践REST的,所以很容易造成这种误解。
客户端不一定都支持这些HTTP方法吧?
的确有这种情况,特别是一些比较古老的基于浏览器的客户端,只能支持GET和POST两种方法。
在实践上,客户端和服务端都可能需要做一些妥协。例如rails框架就支持通过隐藏参数_method=DELETE来传递真实的请求方法, 而像Backbone这样的客户端MVC框架则允许传递_method传输和设置X-HTTP-Method-Override头来规避这个问题。
统一接口是否意味着不能扩展带特殊语义的方法?
统一接口并不阻止你扩展方法,只要方法对资源的操作有着具体的、可识别的语义即可,并能够保持整个接口的统一性。
像WebDAV就对HTTP方法进行了扩展,增加了LOCK、UPLOCK等方法。而github的API则支持使用PATCH方法来进行issue的更新,例如:
PATCH /repos/:owner/:repo/issues/:number
不过,需要注意的是,像PATCH这种不是HTTP标准方法的,服务端需要考虑客户端是否能够支持的问题。