理解 RESTful

作者: 独木舟的木 | 来源:发表于2019-07-05 16:12 被阅读11次

REST 的定义

REST 一词是由 Roy Fielding 博士于 2000 年在他的博士论文 Architectural Styles and the Design of Network-based Software Architecture 中提出的,实际指一种有助于创建和组织分布式系统的架构风格。它并不是一个标准或准则,而是一种基于资源的架构风格

基于 REST 风格构建的 API 应该满足 CS 模式交互、统一的资源接口、透明的分层系统以及支持无状态和缓存等条件约束,另外需要保证 API 的安全性等。

以 REST 风格构建的系统将在性能、组件交互的可扩展性、接口的简单性、可移植性、可靠性、可见性等多个方面得到提升和优化。

——摘自《RESTful API 开发实战》

注:Roy Fielding 是 HTTP 协议(1.0 版和 1.1 版)的主要设计者、Apache 服务器软件的作者之一、Apache 基金会的第一任主席。

REST = Representational State Transfer,表述性状态传递/表现层状态转移。

REST 描述网络中服务器和客户端的交互形式。

资源在网络中以某种表现形式进行状态转移。分解开来:

Resource:资源,即数据。比如 newsfeed,friends 等;
Representational:某种表现形式,比如用 JSON,XML,JPEG 等;
State Transfer:状态变化。通过 HTTP 动词实现。

  1. 每一个 URI 代表一种资源;
  2. 客户端和服务器之间,传递这种资源的某种表现层(资源的呈现形式);
  3. 客户端通过四个 HTTP 动词,对服务器端资源进行操作,实现 "表现层状态转化"。

总结:用 URL 定位资源,用 HTTP 动词(GET、POST、PUT、DELETE)描述操作,实现资源的状态转换。

REST 的基本原理

REST 基于资源架构,资源可通过基于 HTTP 标准方法的通用接口访问。REST 要求开发人员显式使用 HTTP 方法,并以符合协议定义的方式使用。每个资源都有一个 URL 标识,且都应该支持 HTTP 的通用操作,同时 REST 允许该资源有不同的表现形式,例如文本、XML、JSON 等。REST 客户端可以通过 HTTP 协议(内容协商,服务端以正确的格式响应内容)请求特定的表现形式。

数据元素 描述
资源 超文本引用的概念目标,例如 customer/order
资源标识符 统一资源定位符(URL)或统一资源名称(URN)标识特定的资源
资源元数据 描述资源信息,如标签、作者、源链接、替代位置、别名
表现形式 资源内容——JSON 消息、HTML 文档、JPEG 图片
表现元数据 描述如何处理表现的信息,如媒介类型、最后修改时间
控制数据 描述如何优化响应处理的信息,例如 if-modified-since、cache-control-expiry

操作类型

通过 HTTP 动词来操作资源。

HTTP 方法 描述
GET 从服务器上获取资源(一项或多项)
POST 在服务器新建一个资源
PUT 在服务器更新资源(更新资源整体)
PATCH 在服务器更新部分资源(更新资源的某一项属性)
DELETE 从服务器删除资源
HEAD(不常用) 获取资源的元数据(数据的哈希值、最后更新时间...)
OPTIONS(不常用) 获取信息,关于资源的哪些属性是客户端可以操作的

安全性和幂等性

安全性:不会改变资源状态,可以理解为只读的;

幂等性:无边际效应,多次操作得到相同的结果。

当向系统发送 GETDELETEPUT 方法时,不论该方法发送多少次,执行的效果应该是一样的。但是 POST 方法在集合中创建了实体,因此不是幂等的。

HTTP 动词 安全性 幂等性
GET
POST
PUT
DELETE

安全性和幂等性均不保证反复请求能拿到相同的 response。以 DELETE 为例,第一次 DELETE 返回 200 表示删除成功,第二次返回 404 提示资源不存在,这是允许的。

预期的返回结果

当使用不同的 HTTP 动词向服务器发送请求时,服务器向客户端返回的结果应该符合以下规范:

HTTP 方法 路径 response 格式
GET /collection 返回资源对象的列表(数组)
GET /collection/resource 返回单个资源对象
POST /collection 返回新生成的资源对象
PUT /collection/resource 返回完整的资源对象
PATCH /collection/resource 返回完整的资源对象
DELETE /collection/resource 返回一个空文档

API 设计策略

  1. 螺栓策略;
  2. 绿地策略;
  3. 敏捷策略;
  4. 外观策略;

RESTful API 设计的最佳实践

1. 保持基础 URL 简明直观

简明直观的 URL 设计可以体现 API 的功能可见性(接口交互双方不需要文档就可以理解使用这一设计特性)。

对于 Web API 而言,每个资源都应该只有两个基础 URL:

序号 基础URL 描述
1 /customers 集合
2 /customers/{id} 集合中的某个特定元素

使用 HTTP 动词对资源集合和元素进行操作:

资源 POST 创建 GET 读取 PUT 更新 DELETE 删除
/customers 新建客户 客户列表 批量更新 删除所有
/customers/{id} -- 展示某个客户 客户存在就更新,不存在就报错 删除某个客户

2. 错误处理

优秀的异常和错误处理设计对 API 设计者来说非常重要。

一个 API 应当以一种已知的可使用的格式来提供有用的错误信息。

API 应当总是返回有意义的 HTTP 状态代码。API 错误通常被分成两种类型:代表客户端问题的 400 系列状态码和代表服务器问题的 500 系列状态码。最简情况下,API 应当把便于使用的 JSON 格式作为 400 系列错误的标准化表示。如果可能 (意思是,如果负载均衡和反向代理能创建自定义的错误实体), 这也适用于 500 系列错误代码。

{
  "code" : 1234,
  "message" : "Something bad happened",
  "description" : "More details about the error here"
}

对 PUT, PATCH 和 POST 请求进行错误验证将需要一个字段分解。下面可能是最好的模式:使用一个固定的顶层错误代码来验证错误,并在额外的字段中提供详细错误信息,就像这样:

{
  "code" : 1024,
  "message" : "Validation Failed",
  "errors" : [
    {
      "code" : 5432,
      "field" : "first_name",
      "message" : "First name cannot have fancy characters"
    },
    {
       "code" : 5622,
       "field" : "password",
       "message" : "Password cannot be blank"
    }
  ]
}

3. HTTP 状态码

一个请求是否成功是由 HTTP 状态码标明的。一个 2XX 的状态码表示成功,而一个 4XX 表示请求失败。

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]:用户请求的资源被永久删除,且不会再得到的。表示资源在终端不再可用。
415 Unsupported Media Type (不支持的媒体类型) - 如果请求中包含了不正确的内容类型
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
429 Too Many Requests (请求过多) - 当请求由于访问速率限制而被拒绝时

/**
* 在接口处理发生错误的时候,如果是客户端请求参数导致的错误,返回 4xx 状态码,
* 如果是服务端自身的处理逻辑错误,返回 5xx 状态码。
* 所有的异常对象都是对这个异常状态的描述,其中 error 字段是错误的描述,detail 字段(可选)是导致错误的详细原因。
*/
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功
533 INTERNAL SERVER ERROR - [*]:keystore 不存在
534 INTERNAL SERVER ERROR - [*]:keystore 已过期

HTTP 状态码参考:

4. 版本控制

一个好的 RESTful API 会在 URL 中包含版本信息。另一种比较常见的方案是在 HTTP 请求头(Accept 字段)中包含版本信息。但是与很多不同的第三方开发者一起工作之后,我可以很明确的告诉你,在 HTTP 请求头中包含版本信息远没有将其放在 URL 中来的容易(方便),而且将版本号放在 URL 中也更加直观明了。

  • API 版本必须是强制性的。
  • 向后兼容性:必须在引入新版本 API 的同时保持旧版本 API 仍然可用。

常见的根 URL 示例:

// 将 API 放在主域名下:
https://www.example.com/api/v1/*
https://www.example.com/api/v2/*

// 将 API 部署在专用域名之下:
https://api.example.com/v1/*
https://api.example.com/v2/*

5. 过滤、排序和搜索

API 应该向开发人员提供他们需要的信息,而不是所有信息。

复杂查询可以带上过滤条件,常见参数:

常见过滤类型 参数示例 描述
偏移 ?offset=10 指定返回记录的开始位置
限制 ?limit=10 指定返回记录的数量
页数 ?page=2&per_page=100 指定第几页,以及每页的记录数
排序 ?sortby=name&order=asc 指定返回结果按照哪个属性排序,以及排序顺序。
过滤 ?animal_type_id=1 指定筛选条件
?type=1&age=16 指定筛选条件
投影 ?whitelist=id,name,email
局部响应 ?fields=title,media 仅返回部分信息,标题和内容

6. 多格式支持(只返回 JSON)

数据交换格式:建议使用基于 JSON 的媒体类型(Content-Type: application/json

POST /v1/animal HTTP/1.1
Host: api.example.org
Accept: application/json
Content-Type: application/json
Content-Length: 24

{   
  "name": "Gir",
  "animalType": "12"
}

不推荐使用 XML 作为数据交换格式。

Google 趋势图,比较 XML 和 JSON API

7. 端点(EndPoints)

一个端点就是指向特定资源或资源集合的 URL。

API 端点的名词应该使用复数而不是单数。

如果你正在构建一个虚构的 API 来展现几个不同的动物园,每个动物园又包含动物、员工和每个动物的物种,你可能会有如下的端点信息:

针对每个端点,你可能想列出所有可行的 HTTP 动词和端点的组合:

HTTP 方法 路径 描述
GET /zoos List all Zoos (ID and Name, not too much detail)
GET /zoos/ZID Retrieve an entire Zoo object
POST /zoos Create a new Zoo
PUT /zoos/ZID Update a Zoo (entire object)
PATCH /zoos/ZID Update a Zoo (partial object)
DELETE /zoos/ZID Delete a Zoo
GET /zoos/ZID/animals Retrieve a listing of Animals (ID and Name).
GET /animals List all Animals (ID and Name).
POST /animals Create a new Animal
GET /animals/AID Retrieve an Animal object
PUT /animals/AID Update an Animal (entire object)
PATCH /animals/AID Update an Animal (partial object)
GET /animal_types Retrieve a listing (ID and Name) of all Animal Types
GET /animal_types/ATID Retrieve an entire Animal Type object
GET /employees Retrieve an entire list of Employees
GET /employees/EID Retreive a specific Employee
GET /zoos/ZID/employees Retrieve a listing of Employees (ID and Name) who work at this Zoo
POST /employees Create a new Employee
POST /zoos/ZID/employees Hire an Employee at a specific Zoo
DELETE /zoos/ZID/employees/EID Fire an Employee from a specific Zoo

Egg.js 示例

使用 Node.js 的 Egg.js 框架编写 RESTful API 示例。

Method Path Router Name Controller.Action Description
GET /posts posts app.controllers.posts.index 返回所有资源
GET /posts/new new_post app.controllers.posts.new ???
GET /posts/:id post app.controllers.posts.show 返回一个资源
GET /posts/:id/edit edit_post app.controllers.posts.edit ?修改某一条条目
POST /posts posts app.controllers.posts.create 新建一个资源
PUT /posts/:id post app.controllers.posts.update 更新资源
DELETE /posts/:id post app.controllers.posts.destroy 删除资源

app/router.js

module.exports = app => {
  const { router, controller } = app;
  // app.resources('routerName', 'pathMatch', controller);
  router.resources('posts', '/api/posts', controller.posts);
};

app/controller/posts.js

// app/controller/posts.js

// GET - 查询所有记录
exports.index = async () => {};

// GET - 查询某一条资源明细
exports.new = async () => {};

// POST - 增
exports.create = async () => {};

// GET - 查询某一条记录
exports.show = async () => {};

// ?
exports.edit = async () => {};

// PUT - 更新
exports.update = async () => {};

// DELETE - 删除
exports.destroy = async () => {};

名词释义

常见的数据格式:

  • XML(Extensible Markup Language,可扩展标记语言);
  • JSON(JavaScript Object Notation,JavaScript 对象表示法);

常见的传输协议或 Web 服务:

  • SOAP(Simple Object Access Protocol,简单对象访问协议);
  • REST(Representational State Transfer,表述性状态传递);

其他:

  • API(Application Programming Interface,应用程序编程接口);
  • APX(Application Programming Experience,应用编程体验);
  • HATEOAS(Hypermedia as the Engine of Application State, 超媒体作为应用程序状态引擎)

参考

相关文章

  • RESTful API 使用解读

    理解 RESTFul 架构 RESTful API 设计指南

  • restful

    理解RESTful架构

  • python(12)实践Django-Restful API

    关于Restful API,可阅读理解RESTful架构和RESTful API 设计指南。 在Django中要实...

  • RESTful架构和API设计

    理解RESTful风格 - 阮一峰RESTful设计风格指南 - 阮一峰RESTful 风格api 优点: 将接口...

  • RESTful 理解

    RESTful是什么? REST是英文representational state transfer(表象性状态转...

  • RESTful 理解

    什么是RESTful RESTful是一种设计原则。只要我们的服务满足这种设计原则,那我们的服务便可以称之为RES...

  • 理解 RESTFul

    lj

  • 理解 RESTful

    前言 近十年,前端高速发展,整个互联网应用经历了从轻客户端到重客户端的变化,随着前端规模越来越大,交互越来越复杂,...

  • 理解 RESTful

    REST 的定义 REST 一词是由 Roy Fielding 博士于 2000 年在他的博士论文 Archite...

  • restful 理解。

    restful是用来描述网络资源,使用GET,POST,PUT,DELETE请求来区分对资源的不同操作。 但是很多...

网友评论

    本文标题:理解 RESTful

    本文链接:https://www.haomeiwen.com/subject/lsuehctx.html