「翻译」什么是REST?

原文地址:What is REST? By Codecademy

学习使用REST范式来设计网页应用。

Representational State Transfer(表述性状态传递)

REST,全称REpresentational State Transfer(表述性状态传递)是一种结构风格,它为Web上的计算机系统提供了一个标准,使得计算机之间的通信更为简单。符合REST范式的系统,通常被称为RESTful系统,最大的特点是无状态,以及将客户端和服务器分离开。接着我们会来探讨这些术语是什么意思,以及它们能给Web应用带来哪些好处。

客户端与服务器分离

在REST结构风格中,客户端和服务器端能够在不了解彼此功能的情况下,分别独立实现各自的功能。这就意味着,客户端的代码能够在任何时候被修改,但并不影响服务器的运行,同样的,服务器端的代码也能够在不影响客户端功能的情况下被修改。

只要两端知道以什么格式的信息来彼此互通,它们就能够保持模块化和分离。通过将用户界面与数据存储分离开,我们通过简化服务器组件来提高跨平台界面的灵活性,提升可扩展性。此外,两端分离允许每一个组件可以独立改进。

通过使用REST接口,不同的客户端能够访问相同的REST入口,执行相同的操作,并收到相同的响应。

无状态

使用REST范式的系统是无状态的,也就是说服务器不用知道客户端当前处于什么状态,同样地,客户端也不必知道服务器的状态。这样,服务器和客户端都能够在不查看以前的信息的情况下,理解收到的任意信息。这种无状态的限制因为使用资源而非命令得到进一步加强。资源是Web名词,它指的是那些你需要存储或者发送给其他设备的对象、文档或者任何东西。

由于REST系统通过对资源的标准操作来完成交互,它们不依赖接口来实现。

这些限制有助于RESTful应用实现可靠性、快速性能和可扩展性,因为即使是在系统运作期间,组件也能够在不影响系统的情况下被管理、升级和重复使用。

现在,我们来看看客户端和服务器之间的通信究竟是如果通过RESTful接口实现的。

客户端与服务器通信

在REST架构中,客户发送获取或修改资源的请求,服务器则回复这些请求。我们来看看标准的发送请求和回应请求的方式。

发送请求

REST要求客户端通过给服务器发送请求来获取或修改服务器上的数据。请求通常包括:

  • HTTP请求类型,明确请求要执行什么操作
  • 请求头,包含客户端的请求信息
  • 资源路径
  • 包含数据的信息体(可选)
HTTP请求类型

在REST系统中,我们请求资源的基本类型有4种:

  • GET——(通过id)检索特定的资源或一组资源
  • POST——创建一个新的资源
  • PUT——(通过id)更新一个特定的资源
  • DELETE——通过id删除一个特定的资源

如果想了解更多关于HTTP请求的内容,可以看看这篇文章:What is CRUD?

请求头和接受参数

在请求头中,客户端会发送它能够从服务器端接收的内容类型。这个被称为Accept字段,它确保服务器不会返回客户端无法理解或处理的数据。内容类型也被称为MIME类型(Multipurpose Internet Mail Extensions, 多用途因特网邮件扩展)。

MIME类型被用来在Accept字段指定内容的类别,其中包括type类别和subtype子类别。它们通过斜线/符号分隔。

比如,一个包含HTML的文本文件就可以用text/html来表示,如果这个文本文件包含的是CSS内容,就是text/css类别。通用的文本文件可以被标记为text/plain。但text/plain也不是万能药,如果客户端指定的是text/css类别,但接收到的是text/plain文件,它也无法识别文件的内容。

其他类别和它们的常用子类别:

  • image——image/png, img/jpeg, image/gif
  • audio——audio/wav, audio/mpeg
  • video——video/mp4,video/ogg
  • application——application/json,application/pdf,application/xml,application/octet-stream

比如,当客户端想要获取服务器端articles路径中的id是23的资源,它就会发送一个这样的请求:

1
2
GET /articles/23
Accept: text/html, application/xhtml

这里,请求头中的Accept字段指的是客户端可以接受text/htmlapplication/xhtml类型的内容。

路径

请求中必须包含执行请求操作的资源路径。在RESTful API中,路径应该让客户端明白在执行什么操作。

通常,路径的第一部分应该以资源的复数形式命名,这样能够让嵌套的路径更易于阅读和理解。

比如一个叫做fashionboutique.com/customers/223/orders/12的路径就很清晰,即使你之前从没见过这个路径,因为它层级清楚,而且使用了描述性的语言。我们可以知道这是在获取id是223号的顾客的id为12的订单。

路径应该根据资源的特异性程度包含定位资源所需的内容。当我们想要获取一组资源时,并不需要在请求中添加id属性;当我们给fashionboutique.com/customers路径发送POST请求时也不用添加额外的标识符,因为服务器会给新对象自动生成一个id

如果我们想要获取一个单独资源,就需要在路径中添加id了。比如,GET fashionboutique.com/customers/:id路径就是检索cutomers资源中id为指定值的资源。DELETE fashionboutique.com/customers/:id是删除customers资源中id为指定值的资源。

响应请求

内容类型

当服务器给客户端发送数据时,响应的头部必须设置content-type。这个content-type头字段告知客户端响应体的数据类型。这些内容类型是MIME类型,和请求头中的accept字段是一样的。服务器响应回去的content-type必须是请求头中accept指定类型中的一种。

比如,客户端请求获取articles资源中id为23的资源,它会发送一个GET请求:

1
2
GET /articles/23 HTTP/1.1
Accept: text/html, application/xhtml

服务器会回复如下响应头:

1
2
HTTP/1.1 200(OK)
Content-Type: text/html

这表示响应体中的数据是遵循客户端要求的那样,以text/html的内容类型返回给客户端的。

响应码

服务器的响应还包括状态码,用来告诉客户端操作成功与否。作为开发者,你不必知道每一个状态码(真的有很多状态码),但你应该知道最常见的,以及如何使用它们。

状态码 含义
200(成功) 这是一个成功的HTTP请求的标准响应
201(创建成功) 这意味着HTTP请求成功创建了一个项目
204(无内容) 这意味着HTTP请求成功,但是响应体中没有内容
400(错误请求) 这意味着请求无法处理,错误原因可能是语法错误、内容过大或客户端的其他错误
403(请求被禁止) 客户端没有请求资源的权限
404(资源不存在) 资源无法找到,可能被删除或者根本不存在
500(服务器内部错误) 当出现意外错误又无法提供具体错误信息时的标准回复

对于不同的HTTP请求类型,请求成功时服务器的响应如下:

  • GET:返回200(成功)
  • POST:返回201(创建成功)
  • PUT:返回200(成功)
  • DELETE:返回204(无内容)如果请求失败,则返回最接近导致错误原因的状态码
请求和响应示例

假设我们有一个部署在fashionboutique.com上的服装店应用,可以用来查看、创建、编辑和删除顾客和订单。我们可以通过HTTP API来允许客户端实现这些功能:

如果我们想要查看所有的顾客,请求内容如下:

1
2
GET http://fashionboutique.com/customers
Accept: application/json

响应头则如下:

1
2
Status Code: 200(OK)
Content-type: application/json

顾客数据会按照application/json的格式在响应中返回给客户端。

通过POST添加一个新的顾客:

1
2
3
4
5
6
7
8
POST http://fashionboutique.com/customers
Body:
{
"customers": {
"name" = "Scylla Buss"
"email" = "scylla.buss@codecademy.org"
}
}

服务器会给这个对象生成一个id,并给客户端返回响应,响应头如下:

1
2
201(CREATED)
Content-type: application/json

想要查看一个顾客的信息可以发送GET请求,指定顾客的id

1
2
GET http://fashionboutique.com/customers/123
Accept: application/json

服务器的响应头如下:

1
2
Status Code: 200(OK)
Content-type: application/json

id为123的顾客的信息就会通过application/json的格式返回来。

我们还可以通过PUT方法来更新一个顾客的新数据:

1
2
3
4
5
6
7
8
PUT http://fashionboutique.com/customers/123
Body:
{
"customers": {
"name" = "Scylla Buss"
"email" = "scyllabuss1@codecademy.com
}
}

服务器会返回一个包含Status Code: 200(OK)的响应头,告诉客户的id是123的顾客的信息已经更新了。

我们也可以通过指定id来删掉某个顾客:

1
DELETE http://fashionboutique.com/customers/123

服务器会返回一个包含Status Code: 204(NO CONTENT)的响应头,告知客户端id是123的数据已经被删掉了,返回的响应体中没有任何内容。

###REST练习

假设我们现在要搭建一个相册网站,这个网站有一个API能够记录用户、地点以及在这些地点拍摄的照片。网站有index.htmlstyle.css文件。每个用户都有用户名和密码。每张照片都有一个地点和一个拍摄者(拍摄照片的用户)。每个地点都有一个名称和一个街道地址。你能不能设计一个REST系统实现这样的功能:

  • 保存用户、照片和地点
  • 获取地点以及根据特定的地点获取照片

从这里开始:

  • 我们要发送什么类型的请求
  • 服务器可能会响应什么
  • 响应的内容类型应该是什么

解决方案:结构

1
2
3
4
5
6
7
{
"user" {
"id": <数字>,
"username“:<字符串>,
"password":<字符串>
}
}
1
2
3
4
5
6
7
{
"photo": {
"id": <数字>,
"venue_id": <数字>,
"author_id": <数字>
}
}
1
2
3
4
5
6
7
{
"venue": {
"id": <数字>
"name": <字符串>
"address": <字符串>
}
}

解决方案: 请求/响应

GET请求

  • 请求:GET /index.html Accept:text/html 响应:200(OK) Content-type: text/html
  • 请求:GET /style.css Accept:text/css 响应:200(OK) Content-type: text/css
  • 请求:GET /venues Accept:application/json 响应:200(OK) Content-type: appliation/json
  • 请求:GET /venues/:id Accept:application/json 响应:200(OK) Content-type: appliation/json
  • 请求:GET /venues/:id/photos/:id Accept:image/png 响应:200(OK) Content-type: image/png

POST请求

  • 请求:POST/users 响应:201(CREATED) Content-type:application/json
  • 请求:POST/venues 响应:201(CREATED) Content-type:application/json
  • 请求:POST/venues/:id/photos 响应:201(CREATED) Content-type:application/json

PUT请求

  • 请求:PUT/users/:id 响应:200(OK)
  • 请求:PUT/venues/:id 响应:200(OK)
  • 请求:PUT/venues/:id/photos/:id 响应:200(OK)

DELETE请求

  • 请求:DELETE/venues/:id 响应:204(NO CONTENT)
  • 请求:DELETE/venues/:id/photos/:id 响应:204(NO CONTENT)