学习如何使用REST范例设计Web服务
- Go打造REST Server【前言】:什么是REST
- Go打造REST Server【一】:用标准库来实现
- Go打造REST Server【二】:用路由的三方库来实现
- Go打造REST Server【三】:用Web框架来实现
- Go打造REST Server【四】:Graphql进阶
REpresentational State Transfer
REST,即REpresentational State Transfer的缩写,是一种架构风格,用于在网络上的计算机系统之间提供标准,使系统之间更容易相互通信。符合REST的系统,通常称为RESTful系统,其特点是它们是无状态的,并且将客户端和服务器的关注点分开。我们将深入探讨这些术语的含义以及为什么它们在Web服务上有优势。
客户端和服务器分离
在REST架构风格中,客户端的实现和服务器的实现可以独立完成,彼此不知道对方。这意味着客户端的代码可以随时更改而不影响服务器的运行,而服务器端的代码可以更改而不影响客户端的运行。
只要双方都知道要发送给对方的消息格式,它们就可以保持模块化和分离。将用户界面关注点与数据存储关注点分开,我们提高了跨平台界面的灵活性,并通过简化服务器组件来提高可扩展性。此外,分离允许每个组件独立发展的能力。
通过使用REST接口,不同的客户端访问相同的REST端点、执行相同的操作并接收相同的响应。
无状态
遵循REST范式的系统是无状态的,这意味着服务器不需要知道客户端处于什么状态,反之亦然。这样,服务器和客户端都可以理解收到的任何消息,即使没有看到以前的消息。这种无状态约束是通过使用资源而不是命令来实现的。资源是Web的名词——它们描述了可能需要存储或发送到其他服务的任何对象、文档或事物。
因为REST系统通过对资源的标准操作进行交互,所以它们不依赖于接口的实现。
这些约束有助于RESTful应用程序实现可靠性、快速性能和可扩展性,作为可以在不影响整个系统的情况下进行管理、更新和重用的组件,即使在系统运行期间也是如此。
现在,我们将探讨在实现RESTful接口时客户端和服务器之间的通信实际上是如何发生的。
客户端和服务器之间的通信
在REST架构中,客户端发送请求以检索或修改资源,服务器发送对这些请求的响应。让我们看一下发出请求和发送响应的标准方法。
发出请求
REST要求客户端向服务器发出请求,以便检索或修改服务器上的数据。请求通常包括:
- 一个HTTP动作,它定义了要执行的操作类型
- 一个标头,允许客户端传递有关请求的信息
- 资源路径
- 包含数据的可选消息正文
HTTP动作
在请求中最常使用4个基本的HTTP动作来与REST系统中的资源进行交互:
- GET — 检索特定资源(通过id)或资源集合
- POST — 创建一个新资源
- PUT — 更新特定资源(按id)
- DELETE — 按id删除特定资源
Headers和接受参数
在请求的Headers中,客户端发送它能够从服务器接收的内容类型。这称为Accept字段,它确保服务器不会发送客户端无法理解或处理的数据。内容类型的选项是MIME类型(或多用途 Internet邮件扩展),可以在MDN Web Docs文档中阅读更多信息。
MIME Types,用于指定Accept字段中的内容类型,由一个类型和一个子类型组成。它们由斜线/分隔。
例如,包含HTML的文本文件将指定为text/html类型。如果此文本文件包含CSS,则将其指定为text/css。通用文本文件将表示为text/plain。然而,这个默认值text/plain并不是万能的。如果客户端期待text/css并接收text/plain,它将无法识别内容。
其他类型:
image
—image/png
,image/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的资源的客户端可能会发送如下GET请求:
GET /articles/23
Accept: text/html, application/xhtml
在这种情况下,Accept标头字段表示客户端将接受text/html或application/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
— 检索客户资源中指定id的项目。DELETE fashionboutique.com/customers/:id
— 删除指定id的客户资源中的项目。
发送响应
Content Types
在服务器向客户端发送数据负载的情况下,服务器必须在响应的标头中包含内容类型。此内容类型标头字段提醒客户端它在响应正文中发送的数据类型。这些内容类型是MIME类型,就像它们在请求标头的接受字段中一样。服务器在响应中发回的内容类型应该是客户端在请求的接受字段中指定的选项之一。
例如,当客户端使用GET请求访问文章资源中id为23的资源时:
GET /articles/23
Accept: text/html, application/xhtml
服务器可能会发送回带有响应头的内容:
HTTP/1.1 200 (OK)
Content-Type: text/html
这意味着请求的内容正在响应正文中返回,内容类型为text/html,客户端表示它将能够接受。
Response Codes
来自服务器的响应包含状态代码以提醒客户端有关操作成功的信息。作为开发人员,我们不需要知道每个状态码(其中有很多),但应该知道最常见的状态码以及它们的使用方式:
状态码 | 含义 |
---|---|
200 (OK) | 这是成功的HTTP请求的标准响应 |
201 (CREATED) | 这是成功创建项目的HTTP请求的标准响应 |
204 (NO CONTENT) | 这是成功HTTP请求的标准响应,响应正文中没有返回任何内容 |
400 (BAD REQUEST) | 由于请求语法错误、大小过大或其他客户端错误,无法处理请求 |
403 (FORBIDDEN) | 客户端无权访问此资源 |
404 (NOT FOUND) | 此时找不到资源,它可能已被删除,或尚不存在 |
500 (INTERNAL SERVER ERROR) | 如果没有更具体的信息可用,则意外失败的通用答案 |
对于每个HTTP动作,服务器应在成功时返回预期的状态代码:
- GET:返回 200(确定)
- POST:返回 201(已创建)
- PUT:返回 200(OK)
- DELETE:返回 204 (NO CONTENT) 如果操作失败,返回与遇到的问题相对应的最具体的状态码。
REST实践
假设我们正在建立一个照片收集网站。我们想制作一个API来跟踪用户、场地和这些场地的照片。这个站点有一个index.html和一个style.css。每个用户都有一个用户名和一个密码。每张照片都有一个地点和一个所有者(即拍摄照片的用户)。每个场地都有名称和街道地址。设计一个REST系统:
- 存储用户、照片和场地
- 访问场地并访问某个场地的某些照片
首先应该知道:
- 我们想要提出什么样的要求
- 服务器应该返回什么响应
- 每个响应的内容类型应该是什么
可能的解决方案
模型
{
“user”: {
"id": <Integer>,
“username”: <String>,
“password”: <String>
}
}
{
“photo”: {
"id": <Integer>,
“venue_id”: <Integer>,
“author_id”: <Integer>
}
}
{
“venue”: {
"id": <Integer>,
“name”: <String>,
“address”: <String>
}
}
请求与响应
GET
Request- GET /index.html
Accept: text/html
Response- 200 (OK) Content-type: text/html
Request- GET /style.css
Accept: text/css
Response- 200 (OK) Content-type: text/css
Request- GET /venues
Accept:application/json
Response- 200 (OK) Content-type: application/json
Request- GET /venues/:id
Accept: application/json
Response- 200 (OK) Content-type: application/json
Request- GET /venues/:id/photos/:id
Accept: application/json
Response- 200 (OK) Content-type: image/png
POST
Request- POST /users
Response- 201 (CREATED) Content-type: application/json
Request- POST /venues
Response- 201 (CREATED) Content-type: application/json
Request- POST /venues/:id/photos
Response- 201 (CREATED) Content-type: application/json
PUT
Request- PUT /users/:id
Response- 200 (OK)
Request- PUT /venues/:id
Response- 200 (OK)
Request- PUT /venues/:id/photos/:id
Response- 200 (OK)
DELETE
Request- DELETE /venues/:id
Response- 204 (NO CONTENT)
Request- DELETE /venues/:id/photos/:id
Response- 204 (NO CONTENT)