我觉得学习一个技术, 其实就是要弄明白三件事情: 是什么 (what), 为什么(why), 怎么用(how) 正是所谓的三 W 方法
所以打算总结一个三问系列为了自己学习, 也分享给别人
RESTful 是什么?
REST 是 REpresentational State Transfer 的缩写但是 representational state transfer 仍然很难理解直译的话一般译作表述性状态转移什么鬼?
其实是因为前面主语被去掉了, 应该是 Resource Representational State Transfer 直译的话, 我觉得可以译作具象化的资源状态转换
这只是我的个人理解因为这个本来就是老外的晦涩难懂的论文中的词汇, 并没有一个准确的翻译
那么什么是具象化的资源状态转换呢?
Resource: 资源, 即数据网络上的所以数据都可以被看做是资源, 并且能用一个唯一的 URL 表示比如一条用户信息数据, 一张图片, 一个文件等等;
Representational: 具象化的有人认为这个词是某种表现形式, 比如用 JSON,XML,JPEG 等来表示资源我觉得理解成具象化的可能更好一点, 就是指 HTTP 中的具体动词方法: GET,POST,PUT,PATCH,DELETE 等等;
State Transfer: 状态转换 transfer 一般是翻译成转移, 但是我觉得叫转换在这里更贴切状态转换就是指资源被改变, 比如更新操作, 删除操作其实我也觉得这样翻译挺牵强的, 什么嘛还状态转换
总的来说就是对于网络上的通过 URL 表示的资源, 我们通过具象化的 HTTP 方法 GETPOSTDELETE 等等来操作, 改变资源的状态
如果你还是没看懂(应该是没有), 那可以自行参考大神 Roy Fielding 的毕业论文 REST 就是他提出的这哥们参与设计 HTTP 协议, 也是 Apache web Server 项目的 co-founder
论文地址: Architectural Styles and the Design of Network-based Software Architectures
REST 章节: Fielding Dissertation: CHAPTER 5: Representational State Transfer (REST)
怎么样, 看完是不是更懵了呢?
没关系, 一句话理解就是: 在 HTTP 请求中, 用 URL 定位资源, 用 HTTP 动词 (GET,POST,PUT,PATCH,DELETE) 描述对资源进行什么操作
符合这种设计风格的架构设计, 我们就称之为 RESTful 风格的架构
为什么要用 RESTful?
首先要说明的一点就是, RESTful 是一种设计风格, 不是指导思想, 也不是最佳实践只是有些情况下选用符合 RESTful 的架构确实更好一些不吹不黑
现在的网络时代, 技术飞速发展 SOA 啦, Web Service 啦, 微服务啦, 各种概念各种思想层出不穷客户端也是浏览器, Android,iOS 等都五花八门
那么在前后端分离的思想下, 一般我们都是设计基于 HTTP API 的服务这样的好处是什么呢? 当然是一套 API 各种客户端随便用啦
设计 API 的时候, 我们一般有两种方法:
一种是只要用 GET 请求和 POST 请求就足够了, 把操作放在 URL 上
一种是 RESTful 的方式, URL 只表示资源, 用 HTTP 中不同的请求方法代表不同的操作
假设有一类资源 ResourceXYZ , 对其有增删查改的操作 如果只使用 GET POST 之类的设计方式, 那么很可能会设计以下的请求接口:
- POST .../addResourceXYZ // 新增资源
- POST .../delResourceXYZ // 删除资源
- GET .../getResourceXYZ?resourceId=resourceId // 获取指定 ID 的资源
- POST .../updateResourceXYZ // 更新资源
如果按照 RESTful 的 设计方式, 很可能会设计以下的请求接口:
- POST .../ResourceXYZs // 新增资源
- DELETE .../ResourceXYZ/{resourceId} // 删除资源
- GET .../ResourceXYZ/{resourceId} // 获取指定 ID 的资源
- PUT .../ResourceXYZ/{resourceId} // 更新资源
那么使用 RESTful 风格有什么好处呢?
现在假设, 客户端要获取该资源, 其 ID 为 resourceId 如果成功, 那么一切都好说 如果失败, Restful 的处理方式是, 通过 HTTP status 返回错误码来表示原因, 例如 404 表示该资源不存在
那么只用 GET POST 两种方法的方式呢? 响应请求
GET .../getResourceXYZ?resourceId=resourceId
的时候能不能也用 404 呢?
按照 404 的语义, 响应 404 是不对的: 因为客户端请求的 URL 实际上是正确的, 只是对应的参数没有找到对应的结果很多时候, 就只能靠响应 200 然后返回空数据或者空对象来处理了例如 Content-type 为 application/json 时, 可以返回 {} 或者
- {
- "error": "not found",
- "code": 404
- }
这样就会要求客户端, 必须处理 HTTP 回复的具体内容, 而不能只处理头部 那么客户端要怎么处理这个 json 呢? 要先解析 json , 然后尝试区分这是一个资源的内容, 还是一个错误提示
这样前端的人就比较容易骂街了
如何设计一个 RESTful 的架构呢?
我觉得这篇写的很详细很好了, 这里全文转载阮一峰的 RESTful API 设计指南
以下为转载开始:
一协议
API 与用户的通信协议, 总是使用 HTTPs 协议
二域名
应该尽量将 API 部署在专用域名之下
https://api.example.com
如果确定 API 很简单, 不会有进一步扩展, 可以考虑放在主域名下
https://example.org/api/
三版本(Versioning)
应该将 API 的版本号放入 URL
https://api.example.com/v1/
另一种做法是, 将版本号放在 HTTP 头信息中, 但不如放入 URL 方便和直观 Github 采用这种做法
四路径(Endpoint)
路径又称 "终点"(endpoint), 表示 API 的具体网址
在 RESTful 架构中, 每个网址代表一种资源(resource), 所以网址中不能有动词, 只能有名词, 而且所用的名词往往与数据库的表格名对应一般来说, 数据库中的表都是同种记录的 "集合"(collection), 所以 API 中的名词也应该使用复数
举例来说, 有一个 API 提供动物园 (zoo) 的信息, 还包括各种动物和雇员的信息, 则它的路径应该设计成下面这样
- https://api.example.com/v1/zoos
- https://api.example.com/v1/animals
- https://api.example.com/v1/employees
五 HTTP 动词
对于资源的具体操作类型, 由 HTTP 动词表示
常用的 HTTP 动词有下面五个(括号里是对应的 SQL 命令)
GET(SELECT): 从服务器取出资源(一项或多项)
POST(CREATE): 在服务器新建一个资源
PUT(UPDATE): 在服务器更新资源(客户端提供改变后的完整资源)
PATCH(UPDATE): 在服务器更新资源(客户端提供改变的属性)
DELETE(DELETE): 从服务器删除资源
还有两个不常用的 HTTP 动词
HEAD: 获取资源的元数据
OPTIONS: 获取信息, 关于资源的哪些属性是客户端可以改变的
下面是一些例子
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: 删除某个指定动物园的指定动物
六过滤信息(Filtering)
如果记录数量很多, 服务器不可能都将它们返回给用户 API 应该提供参数, 过滤返回结果
下面是一些常见的参数
?limit=10: 指定返回记录的数量
?offset=10: 指定返回记录的开始位置
?page=2&per_page=100: 指定第几页, 以及每页的记录数
?sortby=name&order=asc: 指定返回结果按照哪个属性排序, 以及排序顺序
?animal_type_id=1: 指定筛选条件
参数的设计允许存在冗余, 即允许 API 路径和 URL 参数偶尔有重复比如, GET /zoo/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的
七状态码(Status Codes)
服务器向用户返回的状态码和提示信息, 常见的有以下一些(方括号中是该状态码对应的 HTTP 动词)
200 OK - [GET]: 服务器成功返回用户请求的数据, 该操作是幂等的(Idempotent)
201 CREATED - [POST/PUT/PATCH]: 用户新建或修改数据成功
202 Accepted - [*]: 表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]: 用户删除数据成功
400 INVALID REQUEST - [POST/PUT/PATCH]: 用户发出的请求有错误, 服务器没有进行新建或修改数据的操作, 该操作是幂等的
401 Unauthorized - [*]: 表示用户没有权限(令牌用户名密码错误)
403 Forbidden - [*] 表示用户得到授权(与 401 错误相对), 但是访问是被禁止的
404 NOT FOUND - [*]: 用户发出的请求针对的是不存在的记录, 服务器没有进行操作, 该操作是幂等的
406 Not Acceptable - [GET]: 用户请求的格式不可得(比如用户请求 JSON 格式, 但是只有 XML 格式)
410 Gone -[GET]: 用户请求的资源被永久删除, 且不会再得到的
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时, 发生一个验证错误
500 INTERNAL SERVER ERROR - [*]: 服务器发生错误, 用户将无法判断发出的请求是否成功
状态码的完全列表参见这里
八错误处理(Error handling)
如果状态码是 4xx, 就应该向用户返回出错信息一般来说, 返回的信息中将 error 作为键名, 出错信息作为键值即可
- {
- error: "Invalid API key"
- }
九返回结果
针对不同操作, 服务器向用户返回的结果应该符合以下规范
GET /collection: 返回资源对象的列表(数组)
GET /collection/resource: 返回单个资源对象
POST /collection: 返回新生成的资源对象
PUT /collection/resource: 返回完整的资源对象
PATCH /collection/resource: 返回完整的资源对象
DELETE /collection/resource: 返回一个空文档
十 Hypermedia API
RESTful API 最好做到 Hypermedia, 即返回结果中提供链接, 连向其他 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 的设计被称为 HATEOASGithub 的 API 就是这种设计, 访问 api.github.com 会得到一个所有可用 API 的网址列表
- {
- "current_user_url": "https://api.github.com/user",
- "authorizations_url": "https://api.github.com/authorizations",
- // ...
- }
从上面可以看到, 如果想获取当前用户的信息, 应该去访问 api.github.com/user, 然后就得到了下面结果
- {
- "message": "Requires authentication",
- "documentation_url": "https://developer.github.com/v3"
- }
上面代码表示, 服务器给出了提示信息, 以及文档的网址
十一其他
(1)API 的身份认证应该使用 OAuth 2.0 框架
(2)服务器返回的数据格式, 应该尽量使用 JSON, 避免使用 XML
转载结束
支持 RESTful 的 Java 开发框架, 有 SpringMVC,Oracle 的 Jersey 等关于 Jersey 我还在学习, 以后会写专门的文章来总结
参考文章:
知乎用户覃超的回答: https://www.zhihu.com/question/28557115/answer/48094438
V2EX 用户 noli 的文章: https://www.v2ex.com/t/340607?p=2
阮一峰的博客: http://www.ruanyifeng.com/blog/2014/05/restful_api
来源: https://www.cnblogs.com/xxnn/p/8696864.html