所有文章 > API产品 > REST API 与 Node.js
REST API 与 Node.js

REST API 与 Node.js

REST 是一种强大且普遍存在的 Web 应用程序开发范例。它提供了巨大的好处,可以帮助任何服务变得更高效、更可扩展、更可伸缩。

Node.js 是构建快速、高效且可扩展的 API 的流行解决方案。它具有高度可扩展性,提供了一个可实现快速、统一开发的软件包生态系统。

将 REST 与 Node.js 相结合可以产生一个强大的产品,该产品既稳定又高效、可扩展且可扩展。然而,非常重要的是做出真正的 RESTful 输出——采用 REST 只是成为 RESTful 的第一步,通往 RESTful 设计的道路有多多,路上就有多少陷阱。今天,我们将了解什么是 REST、什么是 RESTful,以及如何开始使用 Node.js 进行 RESTful 开发。

什么是REST?

REST(即表述性状态转移)是一种开发系统的方法。本质上,它允许系统通过无状态模式相互通信,其中共享资源的表示而不是资源本身。 RESTful 实现中的每次交互都单独表达状态表示,这允许跨各种实现进行高效、无缝的交互。

REST基础知识

REST(即表述性状态转移)是 Roy Fielding 在著名的开创性 REST论文中引入的。它概述了一种以状态转移范式为中心的网络通信新方法。尽管 REST 可以包含各种属性,但其本质可以概括为关键原则:

  • REST 利用“客户端-服务器关系”。在此模型中,客户端和服务器在不同的域中运行,各自管理自己的状态。交互通过简单的交换发生:客户端请求,服务器响应。这种独特的通信模式支撑着 REST 架构。
  • REST 是“无状态”的。这里,“状态”是指资源的当前状况或数据。在无状态框架中,客户端处理状态管理。因此,每个服务器请求必须携带所有需要独立处理和响应的信息,确保请求是自给自足的。
  • REST 有一个“统一的接口”。一致性是 RESTful 接口的关键 – 所有组件都遵循标准接口,确保交互的一致性,无论特定服务或资源如何。这包括遵守资源的单一 URI,并在适用的情况下合并其他引用或数据。在上下文中添加引用或数据是一个称为 HATEOAS(超媒体作为应用程序状态引擎)的概念,并且被认为是 RESTful 设计的要求。
  • REST 必须是“可缓存的”。有效的缓存管理在 REST 中至关重要。服务器将数据指定为可缓存或不可缓存,这会显着影响性能和用户体验。
  • REST 是分层的。 RESTful 组件支持分布式架构,允许客户端不直接与终端服务器交互,而是通过各种中间服务和微服务进行交互。

是什么让 RESTful API 变得 RESTful?

是什么让事物真正变得宁静?一种好的方法是使用理查森成熟度模型。 Leonard Richardson 的这个模型使我们能够使用标准的标题来考虑项目的开发阶段,将服务分为几个类别:

Level 0:POX沼泽

这是模型的最低层,表示具有接受所有输入的 URI 的 API。它只是名义上的 API,没有任何将其视为 RESTful 的东西。因此,“0 级是非 RESTful”。

 Level 1:资源

在此级别定义了资源,用户可以发出资源 URI 的请求。您不是要求远程资源或函数执行某些操作,而是要求资源的方法执行该操作。 ”’1 级接近休息”’。

 Level 2:HTTP 动词

第 2 级引入了 HTTP 动词,即 Web 和 RESTful API 的底层用语。在第 2 级中,HTTP 动词具有特定的含义、形式和功能,而在较低级别中,这些动词通常用于多种功能。然而,在级别 2 中,GET 表示 GET,POST 表示 POST,等等,并且它们不会混为一谈。由于 REST 需要缓存,而 HTTP 仅使用适当的措辞进行缓存,因此使用“第 2 级是通过缓存接近 REST”。

Level 3:超媒体控件

在第 3 级,我们看到添加了 HATEOAS(或超文本)作为应用程序状态引擎。 HATEOAS 允许与其他上下文或资源的关系链接,并且是 REST 的要求。 “Level 3  is Likely RESTful’”。

什么是“ Likely RESTful’”?

为什么Level 3 只是“可能是 RESTful”?原始论文中有一些要求在应用中有些变化。因此,某些内容可能处于第 3 级,但仍然缺少一些非常具体的功能。为了真正实现 RESTful,您需要满足原始论文的所有要求 – 因此,RESTful 应该包括达到理查森成熟度模型的第 3 级,同时还正确实现原始论文的其他特征!

 什么是 Node.js?

Node.js 是一种开源、跨平台的 JavaScript 实现,专门为服务器端脚本设计,允许代码在浏览器外部执行。它支持多种平台,并以“JavaScript Everywhere”的概念设计,通过采用 Web 应用程序和使用这些应用程序的客户端的通用语言来解锁客户端和服务器端开发之间的同步性。

值得注意的是,Node.js 是事件驱动的,支持实时、同步通信以及异步通信。这使得 Node.js 能够支持大量潜在的开发选项,并为利用同步和异步模式的混合环境提供统一的方法。

Node.js 具有一些使其成为不错选择的关键功能:

  • Node.js 使用非阻塞事件驱动架构设计,但具有启用阻塞功能的库。本质上,这为使用 JavaScript 的同步和异步开发提供了统一的选项。
  • 由于底层技术是事件驱动和非阻塞的,Node.js 可以同时处理大量连接,从而在网络和应用程序通信中提供极高的效率。
  • 由于它基于 Chrome 的 V8 JavaScript 引擎构建,因此 JavaScript 可以直接编译为机器代码,从而释放出相当高的性能、令人难以置信的速度和高效率。
  • Node.js 附带 npm(节点包管理器),这是一个由开源库和扩展组成的大型生态系统。该管理器还管理利用该生态系统带来的各种依赖项、安装过程、更新等,从而简化了开发生命周期。
  • 轻松实现全生命周期集成。由于 Node.js 使用 JavaScript 统一了客户端和服务器端代码的开发流程,因此开发和维护可以统一到同一个生命周期流程中。

这些优势使 Node.js 成为快速、可扩展、高效且具有简化生命周期的网络应用程序的流行选择。

如何使用 Node.js 创建 RESTful API

首先,我们将使用 Express 框架。 Express 是一个极简、灵活且高效的 Node.js 框架。通过将 Node.js 和 Express 结合在一起,您可以解锁许多额外的设计模式和功能,而开销或对速度的影响很小。

设置您的开发环境

本指南假设您已经安装了 Node.js。如果尚未安装,请导航至Node.js 网站并确保已安装它。从这里,您需要安装 Express。

 安装 Express

要安装express,您需要创建一个director来保存您的Node.js应用程序。使用 mkrdir 执行此操作,如下所示:

mkdir restnode

接下来,使用 cd 命令将工作目录更改为这个新位置:

cd restnode

从这里,您将使用 Node.js 的内置包管理器 npm 为您的应用程序创建 pack.json 文件。

npm init

虽然此过程可以使用默认设置,但 Express 在其文档中指出,您需要设置入口点变量来更改主文件名。我们将使用标准默认值 – 这将生成 index.js,这是我们将构建服务的地方。如果您想更改此设置,请编辑以下代码:

entry point: (yourname.js)

最后,我们安装express:

npm install express

 创建 API 框架

首先,我们必须首先建立 API 的基本框架。为此,我们必须按如下方式编辑 JavaScript:

const express = require(‘express’);
const app = express ();

这将为我们的服务奠定基础,并以 Express 作为我们的框架。接下来,我们需要告诉应用程序我们正在使用一些关键部分:

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use("/api/users", require("./routes/api/users"));

这段代码做了一些事情,但它所做的最重要的事情是通过提供数据存储方法来启用 POST 和 PUT 请求。 “app.use(express.json());”允许我们的服务处理 JSON 对象,并且“app.use(express.urlencoded({ Extended: false }));”允许我们将数据处理为字符串或数组。

现在我们已经创建了骨架,我们需要实际设置一个监听端口。为此,请使用以下代码:

app.listen(3000, () => console.log('Ready'));

此代码指示我们的服务侦听端口 3000,并向控制台记录“就绪”状态,这让我们知道它已准备好处理请求。

 创建用户模型

虽然我们可以连接到数据库(有关更多信息,请查看Express 文档) ,但我们将通过使用本地文件来存储用户数据来保持简单。为此,我们必须首先创建 Users.js 文件。

我们可以使用 mkdir 命令再次执行此操作。使用以下代码创建该文件:

touch Users.js

这将创建文件供我们编辑。现在我们有了文件,我们需要创建可供其他系统使用的实际数据结构。为此,请使用以下代码设置数据结构:

const users = [
{
id: 1,
name: "ExampleUser",
email: "exampleuser@website.com"
}
];

有了这个结构,我们可以在末尾添加一个模块导出,以允许其他项目文件使用这个结构。为此,请将以下代码附加到 users.js:

module.exports = users;

创建路由结构

接下来,我们需要创建路线和端点。为此,我们将创建一个新的目录文件夹并将其命名为“routes”,然后创建一个名为“api”的子文件夹。这将使我们能够将所有路由数据存储在单独的目录中,稍微清理一下我们的结构,并明确将来在故障排除期间可能发生故障的位置。您可以在此处使用“mkdir”操作来执行此操作。

在此文件夹中,我们将创建一个名为 users.js 的新 .js 文件。请注意,我们在这里使用了小写“u” – 记住术语和标准很重要,并且由于我们已经使用“Users”同名存储解决方案,因此我们使用“users”来定义它的从属文件,其中包含我们的路线。

 创建端点

在这个文件中,我们需要创建一些逻辑路由。首先,再次声明我们需要 Express:

const express = require(‘express’)

接下来,我们需要使用以下代码定义路由器:

const router = express.Router();

为了确保我们的数据路由清晰并以结构化方式提取数据,我们需要一种为每个用户实体生成唯一 ID 的方法。为此,首先使用 npm 在我们的核心目录中安装“uuid”包:

npm install uuid

回到我们的 Users.js 文件,我们可以使用以下命令调用这个“uuid 包”:

const uuid = require("uuid");

接下来,我们需要设置 API 的路径,以便我们的用户系统可以正确处理这些数据。我们可以使用以下代码来执行此操作:

let users = require("../../Users");

现在我们终于可以创建我们的第一个端点了。我们将创建一个 GET 函数来检索所有用户数据。我们可以使用以下代码来做到这一点:

router.get("/", (req, res) => {
res.json(users);
});

这段代码使用route.get来处理请求,并传递系统中存储的所有当前用户数据。为了获取特定的 ID,我们需要为客户端提供一种传递用户 ID 并检查内部数据存储的方法。我们可以使用以下代码来执行此操作:

router.get("/:id", (req, res) => {
const found = users.some(user => user.id === parseInt(req.params.id));

if (found) {
res.json(users.filter(user => user.id === parseInt(req.params.id)));
} else {
res.sendStatus(400);
}
});

请注意,这里有一个“if”函数,当请求格式错误或引用不存在的数据时,它会提供通用错误代码。

创建此路由后,我们现在可以使用以下代码将其导出以供 API 使用:

module.exports = router;

现在我们有了一个功能性 API,它将根据请求提供用户数据。整洁的!

 实现CRUD功能

从这里,我们可以扩展这些端点以提供各种新功能。 CRUD(即创建、读取、更新、删除)是 RESTful API 的关键概念,并且应该成为应用程序中的核心功能。我们可以使用以下代码启用每个功能。

为了创建用户数据,我们可以使用 POST:

router.post("/", (req, res) => {
const newUser = {
id: uuid.v4(),
name: req.body.name,
email: req.body.email
};

if (!newUser.name || !newUser.email) {
return res.sendStatus(400);
}
users.push(newUser);
res.json(users);
});

为了更新,我们可以使用 PUT:

router.put("/:id", (req, res) => {
const found = users.some(user => user.id === parseInt(req.params.id));
if (found) {
const updateUser = req.body;
users.forEach(user => {
if (user.id === parseInt(req.params.id)) {
user.name = updateUser.name ? updateUser.name : user.name;
user.email = updateUser.email ? updateUser.email : user.email;
res.json({ msg: "User updated", user });
}
});
} else {
res.sendStatus(400);
}
});

对于删除,我们可以使用 DELETE:

router.delete("/:id", (req, res) => {
const found = users.some(user => user.id === parseInt(req.params.id))
if (found) {
users = users.filter(user => user.id !== parseInt(req.params.id))
res.json({
msg: "User deleted",
users
});
} else {
res.sendStatus(400);
}
});

 让它变得RESTful 

这里缺少的一件大事就是 HATEOAS。 HATEOAS(应用程序状态引擎的超媒体)在我们的用户数据中提供关系链接,是 RESTful 设计的子约束。为了实现这一目标,我们可以使用多种选项——对于我们的例子,我们将使用一个非常简单的 Express 扩展,称为“express-hateoas-links”。

首先,首先安装express-hateoas-links:

npm install express-hateoas-links

安装完毕后,我们需要开始向我们的“路由”逻辑添加额外的上下文。例如,对于我们的 GET 逻辑,我们将编辑以下内容:

router.get("/:id", (req, res) => {
const found = users.some(user => user.id === parseInt(req.params.id));

if (found) {
res.json(users.filter(user => user.id === parseInt(req.params.id)));
} else {
res.sendStatus(400);
}
});

编辑看起来像这样:

router.get("/:id", (req, res) => {
const foundUser = users.find(user => user.id === parseInt(req.params.id));

if (foundUser) {
const userWithLinks = {
...foundUser,
links: [
{ rel: "self", method: "GET", href: `/users/${foundUser.id}` },
{ rel: "all-users", method: "GET", href: "/users" },
{ rel: "update-user", method: "PUT", href: `/users/${foundUser.id}` },
{ rel: "delete-user", method: "DELETE", href: `/users/${foundUser.id}` }
]
};

res.json(userWithLinks);
} else {
res.sendStatus(400);
}
});

此更新将添加一些相关链接。 “rel”将建立每个上下文链接的关系,然后根据用于与原始节点交互的特定方法提供附加上下文链接。虽然这些关系链接仅用于功能扩展,但也可以通过此方法提供其他关系链接,例如有关特定人员的上下文、其组织的链接等。

 最佳实践

认证与授权

基于最小权限原则在您的服务中实施身份验证和授权措施至关重要。这种方法涉及将用户的访问权限限制在执行任务所需的最低限度。仔细管理对资源的访问非常重要,确保它们尽可能安全。

谨慎使用超媒体;虽然这可能是有益的,但不加区别地暴露所有资源是不可取的。努力共享有用且​​安全的数据,最大限度地减少滥用或泄露的风险。

 API结构

设计 RESTful API 时,面向资源非常重要。每个资源都应该与执行不同功能的特定 HTTP 动词相关联,并且这些交互应该是幂等的(并且安全,尽管这是一个比本文范围更广泛的主题)。幂等性意味着每个请求应该一致地产生相同类型的响应,即使实际内容可能有所不同。

为客户端提供缓存功能是一个关键考虑因素,但请确保您的缓存不会缓存可能受到保护或用于攻击的信息,例如 API 密钥、用户数据、登录方法等。

 结论

让某些东西变得 REST 很容易,但要使其真正 RESTful 可能会有点复杂。通过一些规划以及基于上下文和关系链接的方法,可以非常轻松地开始制作功能强大、可扩展且稳定的 Node.js 应用程序。