RESTful API

RESTful API

一、RESTful API概述

1.1 定义

REST(Representational State Transfer)即表现层状态转移,是一种针对网络应用的设计风格,主要用于设计分布式超媒体系统。RESTful API 则是遵循 REST 架构原则的应用程序接口,允许客户端和服务器通过 HTTP 协议进行交互。它采用资源定位的思维模式,将所有的操作都视为对资源的操作,以使系统更加简洁、易于理解和扩展。

1.2 核心概念

  • 资源(Resource):RESTful API 中的每一个对象、实体或数据都被抽象为一个资源。例如,用户、文章等都可以作为资源。每个资源都通过一个唯一的 URI(统一资源标识符)标识。比如,/users/123 表示 id123 的用户资源,/posts/456 表示 id456 的文章资源。
  • URI(统一资源标识符):用于标识资源的地址,通常使用 URL(统一资源定位符)作为 URI。
  • HTTP 动作(HTTP Methods):依赖于 HTTP 协议的常见方法来对资源进行操作,每个 HTTP 方法对应不同的操作:
    • GET:获取服务器上的资源。
    • POST:在服务器上创建新的资源。
    • PUT:更新服务器上的资源。
    • DELETE:删除服务器上的资源。
  • 无状态(Statelessness):每个请求都应该是独立的,服务器不会在请求之间保存客户端的状态。每次请求都必须包含理解请求所必需的信息。
  • 表现层状态转移(Representational State Transfer):资源的表现形式可以是 JSON、XML、HTML 等格式,通常 RESTful API 使用 JSON 作为数据交换格式,因为它轻量且易于解析。客户端通过接收资源的表现形式(如 JSON、XML)来感知资源的变化,从而实现状态的“转移”。

二、RESTful API的特点和优势

2.1 特点

  • 资源导向:所有内容都被抽象为资源(如 User、Order、Article 等),每个资源都有一个唯一的标识符(URI)。例如,/users 表示用户资源,/users/1 表示 ID 为 1 的用户。
  • 使用标准协议:基于 HTTP 协议,直接使用其方法(GET、POST 等)来操作资源。
  • 无状态通信:每次请求都包含所有上下文,不依赖服务器保存状态。这使得服务器可以更加容易地进行扩展和负载均衡,因为每个请求都是独立的,不需要考虑之前的请求状态。
  • 统一接口:接口风格统一、易于理解和使用。通过标准的 HTTP 方法(GET、POST、PUT、DELETE 等)和资源标识符(URI)访问资源,简化了系统的整体架构,提升了互操作性。
  • 可缓存:客户端可根据响应头对资源进行缓存,提高性能。利用 HTTP 协议的缓存机制,允许中间件或客户端缓存响应结果,减少不必要的网络请求,从而提升响应速度和减轻服务器负担。
  • 分层架构:客户端无需知道请求最终由谁处理(例如中间层、负载均衡等)。允许通过中间层(如代理服务器、网关)来处理请求,每层只需与相邻层通信,增强了系统的安全性、可扩展性和灵活性。
  • 按需代码(可选):客户端可以从服务器下载代码或脚本,以扩展其功能,增加了系统的灵活性和可扩展性。

2.2 优势

  • 简洁易懂:使用 HTTP 协议的标准方法和 URI,可以让 API 的设计和使用变得简单。通过 URL 和 HTTP 动词的组合,可以明确地表示对特定资源的操作意图。
  • 灵活性:资源可以有不同的表示形式(JSON、XML 等),同时 HTTP 方法明确区分不同的操作。客户端可以根据自身需求选择合适的数据格式。
  • 扩展性强:通过一致的接口设计,可以很容易地扩展和维护 API。随着应用程序的发展,可以轻松地添加新的资源和操作,而不会影响现有的 API 结构。
  • 无状态性:简化了服务器端的设计,增强了系统的可扩展性。服务器不需要保存客户端的状态信息,使得服务器可以更容易地进行扩展和负载均衡。
  • 跨平台:基于 HTTP 协议,可以被任何支持 HTTP 的客户端调用,适用于各种应用场景,特别是需要跨平台和跨语言交互的系统。

三、RESTful API的设计原则

3.1 基于资源

将网络上的每个实体或概念视为唯一资源是一个关键原则。通过 URL 来表示这些资源,使得资源的定位更加清晰和直观。例如,一个博客文章可以被表示为 /articles/123,其中 “articles” 表示文章资源的集合,“123” 是具体某一篇文章的唯一标识符。这种方式使得开发者和用户都能够轻松地理解和访问特定的资源。以电商平台为例,商品可以表示为 /products,每个具体的商品可以通过 /products/{productId} 来访问,其中 {productId} 是商品的唯一标识。

3.2 统一接口

  • 资源标识(Resource Identification):每个资源都应有唯一的 URL。例如,用户资源可以通过 /users/{id} 进行标识,其中 {id} 是用户的具体标识。
  • 资源操作(Resource Manipulation Through Representations):通过表示(representation)来操作资源,而不是直接操作资源本身。使用标准的 HTTP 方法(GET、POST、PUT、DELETE 等)来操作资源。
  • 自描述消息(Self - descriptive Messages):响应消息应包含足够的信息以便客户端无需额外文档即可理解。每个请求和响应都包含足够的信息,使得客户端能够理解如何处理它们。
  • 无状态(Stateless):服务器不保存客户端的上下文信息,每次请求都是独立的。每个请求都必须包含足够的信息,使服务器能够理解和处理该请求,而不依赖于之前的请求。

3.3 使用标准的 HTTP 方法

主要使用 HTTP 方法来定义对资源的操作,常见的 HTTP 方法及其作用如下:

HTTP 方法 操作 幂等性 安全性 示例
GET 获取资源 GET /users 查看所有用户信息;GET /users/id 查看该 id 的用户信息
POST 创建资源 POST /users 创建用户,可在 DATA 处带需要的参数
PUT 更新或替换资源 PUT /users/id?name='张三'&age=20 修改该 id 的用户信息(name 和 age)
PATCH 部分更新资源 对资源进行部分属性的更新
DELETE 删除资源 DELETE /users/id 删除该 id 的用户信息
HEAD 获取资源的元数据
OPTIONS 获取信息,关于资源的哪些属性是客户端可以改变的

3.4 无状态通信

每个请求必须包含服务器处理该请求所需的所有信息,服务器不依赖之前的请求上下文。这使得服务器可以更加容易地进行扩展和负载均衡,因为每个请求都是独立的,不需要考虑之前的请求状态。例如,当客户端发送多个请求时,服务器不需要记住之前的请求内容,只需要根据当前请求的信息进行处理。

3.5 返回适当的状态码

API 应返回适当的 HTTP 状态码,准确反映请求结果。常用的状态码如下:

状态码 含义 说明
200 OK 服务器成功返回用户请求的数据,该操作是幂等的(Idempotent),通用成功。
201 Created 资源已创建,通常用于 POST 请求。
204 No Content 请求成功,但无返回内容,通常用于 DELETE 请求。
400 Bad Request 请求参数有误,服务器无法处理。
401 Unauthorized 认证失败,客户端需要提供身份验证。
403 Forbidden 没有权限访问资源,服务器理解请求,但拒绝执行,通常由于权限问题。
404 Not Found 请求的资源不存在。
500 Internal Server Error 服务器内部错误。

3.6 过滤信息

如果记录数量很多,服务器不可能都将它们返回给用户。API 应该提供参数,过滤返回结果。常见的参数如下:

  • ?limit=10:指定返回记录的数量。
  • ?offset=10:指定返回记录的开始位置。
  • ?page=2&per_page=100:指定第几页,以及每页的记录数。
  • ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
  • ?animal_type_id=1:指定筛选条件。

3.7 支持 HATEOAS(超媒体作为应用程序状态引擎)

HATEOAS 要求客户端能够通过服务器返回的超链接(URL)导航到相关资源。简而言之,HATEOAS 要求 API 的响应不仅包含资源数据,还应该包含与资源相关的操作链接,帮助客户端更好地理解如何进行下一步操作。例如,获取用户信息时,除了返回用户的详细数据外,API 还可以提供相关操作的链接:

1
2
3
4
5
6
7
8
9
{
    "user_id": 123,
    "name": "John Doe",
    "links": {
        "self": "/users/123",
        "update": "/users/123/update",
        "delete": "/users/123/delete"
    }
}

3.8 数据格式

返回数据格式通常使用 JSON 或 XML,其中 JSON 因其轻量级和易读性,已成为 RESTful API 事实上的数据交换格式。统一返回 JSON 格式,包含数据、状态码和错误信息,例如:

1
2
3
4
5
{
    "status": 200,
    "data": { "id": 123, "name": "Alice" },
    "error": null
}

3.9 版本控制

当 API 发生变化时,可以通过版本号来管理不同版本的 API,以保持向后兼容性。常见的版本控制策略有:

  • URL 路径版本:如 /v1/resource,优点是简单、显式、易缓存;缺点是不够优雅、URL 膨胀。
  • 请求头版本:如 X - API - Version: 1,优点是 URL 干净、松耦合;缺点是难调试、可能被代理移除。
  • 内容协商版本:如 Accept: application/vnd.example.v1+json,优点是 RESTful、标准 HTTP 头;缺点是复杂、客户端支持不一。
  • 查询参数版本:如 /resource?version=1,优点是简单、显式;缺点是不是 RESTful、URL 污染。

四、RESTful API的设计示例

以一个简单的博客平台为例,设计其 RESTful API:

4.1 资源列表

  • 用户(/users
  • 文章(/articles
  • 评论(/comments

4.2 操作设计

操作 HTTP 方法 URL 说明
获取所有用户 GET /users 返回所有用户的列表
获取特定用户 GET /users/{id} 返回指定 ID 用户的详细信息
创建用户 POST /users 根据请求体中的数据创建新用户
更新用户信息 PUT /users/{id} 更新指定 ID 用户的信息
删除用户 DELETE /users/{id} 删除指定 ID 的用户
获取所有文章 GET /articles 返回所有文章的列表
获取特定文章 GET /articles/{id} 返回指定 ID 文章的详细信息
创建文章 POST /articles 根据请求体中的数据创建新文章
更新文章信息 PUT /articles/{id} 更新指定 ID 文章的信息
删除文章 DELETE /articles/{id} 删除指定 ID 的文章
获取特定文章的评论 GET /articles/{id}/comments 返回指定 ID 文章的所有评论
创建评论 POST /articles/{id}/comments 在指定 ID 文章下创建新评论
删除评论 DELETE /comments/{id} 删除指定 ID 的评论

五、RESTful API的实现

5.1 服务端实现(以 Node.js 为例)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
const express = require('express');
const app = express();
app.use(express.json());

// 模拟用户数据
const users = [
    { id: 1, name: 'John Doe', email: 'john@example.com' },
    { id: 2, name: 'Jane Smith', email: 'jane@example.com' }
];

// 获取所有用户
app.get('/api/v1/users', (req, res) => {
    res.status(200).json({ status: 200, data: users });
});

// 创建新用户
app.post('/api/v1/users', (req, res) => {
    const newUser = req.body;
    users.push(newUser);
    res.status(201).json({ status: 201, data: newUser });
});

// 获取特定用户
app.get('/api/v1/users/:id', (req, res) => {
    const userId = parseInt(req.params.id);
    const user = users.find(u => u.id === userId);
    if (user) {
        res.status(200).json({ status: 200, data: user });
    } else {
        res.status(404).json({ status: 404, error: 'User not found' });
    }
});

// 更新用户信息
app.put('/api/v1/users/:id', (req, res) => {
    const userId = parseInt(req.params.id);
    const updatedUser = req.body;
    const index = users.findIndex(u => u.id === userId);
    if (index!== -1) {
        users[index] = updatedUser;
        res.status(200).json({ status: 200, data: updatedUser });
    } else {
        res.status(404).json({ status: 404, error: 'User not found' });
    }
});

// 删除用户
app.delete('/api/v1/users/:id', (req, res) => {
    const userId = parseInt(req.params.id);
    const index = users.findIndex(u => u.id === userId);
    if (index!== -1) {
        users.splice(index, 1);
        res.status(204).send();
    } else {
        res.status(404).json({ status: 404, error: 'User not found' });
    }
});

const port = process.env.PORT || 3000;
app.listen(port, () => {
    console.log(`Server is running on port ${port}`);
});

5.2 客户端调用(以 JavaScript Fetch 为例)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 获取用户数据
fetch('https://api.example.com/users/123')
   .then(response => response.json())
   .then(data => console.log(data))
   .catch(error => console.error('Error:', error));

// 提交新文章
fetch('https://api.example.com/articles', {
    method: 'POST',
    headers: { 'Content - Type': 'application/json' },
    body: JSON.stringify({ title: 'Hello REST', content: '...' })
});

六、RESTful API的应用场景

6.1 Web 服务

提供 Web 服务,如社交媒体、电商网站等。前端(如 JavaScript、Angular、React 等)可以通过 RESTful API 与后端服务器进行通信,获取和更新数据。例如,一个电商网站的前端可以使用 RESTful API 从服务器获取商品列表、用户信息等,并将用户的订单信息发送到服务器进行处理。

6.2 移动应用

移动应用通过 RESTful API 与服务器交互,获取和更新数据,实现用户登录、数据同步等功能。Android 和 iOS 应用可以使用 RESTful API 与服务器交互,提供丰富的用户体验。

6.3 物联网(IoT)

设备通过 RESTful API 与服务器通信,实现数据采集和控制。物联网设备可以通过 RESTful API 将采集到的数据发送到服务器,同时接收服务器的控制指令。

6.4 微服务架构

微服务之间通过 RESTful API 进行通信和协作。每个微服务都可以暴露自己的 RESTful API,其他微服务可以通过调用这些 API 来获取所需的数据或执行特定的操作。例如,一个电商系统可能由用户服务、商品服务、订单服务等多个微服务组成,这些微服务之间可以通过 RESTful API 进行数据交互和业务协作。

6.5 企业级应用集成

企业内部的不同系统之间可以使用 RESTful API 进行集成。例如,企业的客户关系管理系统(CRM)和企业资源规划系统(ERP)可以通过 RESTful API 进行数据交换,实现信息共享和业务流程的协同。

七、RESTful API与传统 API的对比

比较项 RESTful API 传统 API 风格
接口风格 资源导向,结构清晰 动作导向,接口混乱
动作表示 使用 HTTP 方法表示动作 接口路径中包含动词(如 /getUser
可读性 高,可直观理解操作含义 低,需要阅读文档才能理解
维护性 易于扩展和维护 扩展性差,接口膨胀
数据传输格式 通常使用 JSON 或 XML,有明确标准 可能使用多种格式,无明确标准
状态与缓存 强调状态无关性,可利用 HTTP 缓存机制 可能依赖服务器端状态,缓存策略不一致
安全性和认证 支持各种安全性措施,如 HTTPS、认证、授权等 可能缺乏统一的安全标准
接口一致性 统一接口,易于使用 接口设计松散,不一致
资源关联性 在响应中提供相关资源的链接,具有自描述性 通常不提供自描述性

综上所述,RESTful API 以其简洁、灵活、可扩展等优点,成为现代 Web 开发中主流的 API 设计风格之一,广泛应用于各种网络应用场景中。在设计和开发 RESTful API 时,遵循其设计原则和最佳实践,能够构建出高效、易用、可维护的 API 系统。

使用 Hugo 构建
主题 StackJimmy 设计