如何使用HTTP模块在Node.js中创建Web服务器
作者选择了 COVID-19 Relief Fund 作为 Write for DOnations 计划的一部分来接受捐赠。
介绍
当您在浏览器中查看网页时,您正在向 Internet 上的另一台计算机发出请求,然后它会向您提供网页作为响应。 您通过互联网与之交谈的那台计算机是 网络服务器 。 Web 服务器从客户端(例如您的浏览器)接收 HTTP 请求,并提供 HTTP 响应,例如 HTML 页面或来自 API 的 JSON。
服务器返回网页涉及到很多软件。 该软件通常分为两类:前端和后端。 前端代码关注的是内容如何呈现,例如导航栏的颜色和文本样式。 后端代码 关注数据的交换、处理和存储方式。 处理来自浏览器的网络请求或与数据库通信的代码主要由后端代码管理。
Node.js 允许开发人员使用 JavaScript 编写后端代码,尽管传统上它在浏览器中用于编写前端代码。 像这样将前端和后端放在一起可以减少创建 Web 服务器所需的工作量,这也是 Node.js 成为编写后端代码的热门选择的主要原因。
在本教程中,您将学习如何使用 Node.js 中包含的 http 模块 构建 Web 服务器。 您将构建可以返回 JSON 数据、CSV 文件和 HTML 网页的 Web 服务器。
先决条件
- 确保 Node.js 安装在您的开发机器上。 本教程使用 Node.js 版本 10.19.0。 要在 macOS 或 Ubuntu 18.04 上安装它,请按照 如何在 macOS 上安装 Node.js 和创建本地开发环境中的步骤或 的 使用 PPA 部分安装如何在 Ubuntu 18.04 上安装 Node.js。
- Node.js 平台支持开箱即用地创建 Web 服务器。 要开始使用,请确保您熟悉 Node.js 的基础知识。 您可以通过查看我们关于 如何在 Node.js 中编写和运行您的第一个程序的指南开始。
- 我们还为我们的部分之一使用异步编程。 如果您不熟悉 Node.js 中的异步编程或与文件交互的
fs
模块,您可以阅读我们的文章 如何在 Node.js 中编写异步代码 了解更多信息.
第 1 步——创建一个基本的 HTTP 服务器
让我们首先创建一个向用户返回纯文本的服务器。 这将涵盖设置服务器所需的关键概念,这将为返回更复杂的数据格式(如 JSON)提供必要的基础。
首先,我们需要设置一个可访问的编码环境来进行我们的练习以及本文中的其他练习。 在终端中,创建一个名为 first-servers
的文件夹:
mkdir first-servers
然后进入那个文件夹:
cd first-servers
现在,创建将容纳代码的文件:
touch hello.js
在文本编辑器中打开文件。 我们将使用 nano
,因为它在终端中可用:
nano hello.js
我们首先加载所有 Node.js 安装标准的 http
模块。 将以下行添加到 hello.js
:
第一服务器/hello.js
const http = require("http");
http
模块包含创建服务器的功能,我们稍后会看到。 如果您想了解有关 Node.js 中模块的更多信息,请查看我们的 如何创建 Node.js 模块 文章。
我们的下一步将是定义两个常量,我们的服务器将绑定到的主机和端口:
第一服务器/hello.js
... const host = 'localhost'; const port = 8000;
如前所述,Web 服务器接受来自浏览器和其他客户端的请求。 我们可以通过输入域名与网络服务器进行交互,域名由 DNS 服务器转换为 IP 地址。 IP 地址是唯一的数字序列,用于标识网络(如 Internet)上的机器。 有关域名概念的更多信息,请查看我们的 DNS 术语、组件和概念简介 文章。
值 localhost
是计算机用来引用自己的特殊私有地址。 它通常等同于内部 IP 地址 127.0.0.1
,它只对本地计算机可用,而不是对我们加入的任何本地网络或互联网可用。
端口是服务器用作我们 IP 地址的端点或“门”的数字。 在我们的示例中,我们将使用端口 8000
作为我们的 Web 服务器。 端口 8080
和 8000
通常用作开发中的默认端口,在大多数情况下,开发人员将使用它们而不是 HTTP 服务器的其他端口。
当我们将我们的服务器绑定到这个主机和端口时,我们将能够通过在本地浏览器中访问 http://localhost:8000
来访问我们的服务器。
让我们添加一个特殊的函数,在 Node.js 中我们称之为 请求侦听器。 此函数旨在处理传入的 HTTP 请求并返回 HTTP 响应。 这个函数必须有两个参数,一个请求对象和一个响应对象。 请求对象捕获传入的 HTTP 请求的所有数据。 响应对象用于为服务器返回 HTTP 响应。
我们希望我们的第一台服务器在有人访问它时返回此消息:"My first server!"
。
接下来让我们添加该函数:
第一服务器/hello.js
... const requestListener = function (req, res) { res.writeHead(200); res.end("My first server!"); };
该函数通常会根据它的作用来命名。 例如,如果我们创建了一个请求侦听器函数来返回书籍列表,我们可能会将其命名为 listBooks()
。 由于这是一个示例案例,我们将使用通用名称 requestListener
。
Node.js 中的所有请求侦听器函数都接受两个参数:req
和 res
(如果需要,我们可以对它们进行不同的命名)。 用户发送的 HTTP 请求被捕获在 Request 对象中,该对象对应于第一个参数 req
。 我们返回给用户的 HTTP 响应是通过与第二个参数 res
中的 Response 对象交互形成的。
第一行 res.writeHead(200);
设置响应的 HTTP 状态码。 HTTP 状态代码指示服务器处理 HTTP 请求的程度。 在这种情况下,状态码 200
对应于 "OK"
。 如果您有兴趣了解 Web 服务器可以返回的各种 HTTP 代码及其含义,我们的 如何对常见 HTTP 错误代码进行故障排除 指南是一个不错的起点。
函数的下一行 res.end("My first server!");
将 HTTP 响应写回请求它的客户端。 此函数返回服务器必须返回的任何数据。 在这种情况下,它返回文本数据。
最后,我们现在可以创建我们的服务器并使用我们的请求监听器:
第一服务器/hello.js
... const server = http.createServer(requestListener); server.listen(port, host, () => { console.log(`Server is running on http://${host}:${port}`); });
按 CTRL+X
保存并退出 nano
。
在第一行中,我们通过 http
模块的 createServer()
函数创建一个新的 server
对象。 该服务器接受 HTTP 请求并将它们传递给我们的 requestListener()
函数。
创建服务器后,我们必须将其绑定到网络地址。 我们使用 server.listen()
方法来做到这一点。 它接受三个参数:port
、host
和一个 回调函数 ,它在服务器开始监听时触发。
所有这些参数都是可选的,但最好明确说明我们希望 Web 服务器使用哪个端口和主机。 将 Web 服务器部署到不同的环境时,需要知道运行它的端口和主机才能设置负载平衡或 DNS 别名。
回调函数将消息记录到我们的控制台,以便我们知道服务器何时开始监听连接。
注意:即使requestListener()
没有使用req
对象,它仍然必须是函数的第一个参数。
使用不到 15 行代码,我们现在拥有了一个 Web 服务器。 让我们看看它的实际效果,并通过运行程序对其进行端到端测试:
node hello.js
在控制台中,我们将看到以下输出:
OutputServer is running on http://localhost:8000
请注意,提示消失了。 这是因为 Node.js 服务器是一个长时间运行的进程。 只有在遇到导致它崩溃并退出的错误,或者我们停止运行服务器的 Node.js 进程时,它才会退出。
在一个单独的终端窗口中,我们将使用 cURL 与服务器通信,这是一个用于在网络之间传输数据的 CLI 工具。 输入命令向我们正在运行的服务器发出 HTTP GET
请求:
curl http://localhost:8000
当我们按下 ENTER
时,我们的终端将显示以下输出:
OutputMy first server!
我们现在已经设置了一个服务器并获得了我们的第一个服务器响应。
让我们分解测试服务器时发生的事情。 使用 cURL,我们向位于 http://localhost:8000
的服务器发送了一个 GET
请求。 我们的 Node.js 服务器监听来自该地址的连接。 服务器将该请求传递给 requestListener()
函数。 该函数返回带有状态码 200
的文本数据。 然后服务器将该响应发送回 cURL,cURL 在我们的终端中显示该消息。
在继续之前,让我们按 CTRL+C
退出正在运行的服务器。 这会中断我们服务器的执行,使我们回到命令行提示符。
在我们访问的大多数网站或我们使用的 API 中,服务器响应很少是纯文本的。 我们将 HTML 页面和 JSON 数据作为常见的响应格式。 在下一步中,我们将学习如何以我们在 Web 中遇到的常见数据格式返回 HTTP 响应。
第 2 步 — 返回不同类型的内容
我们从 Web 服务器返回的响应可以采用多种格式。 之前提到过 JSON 和 HTML,我们还可以返回 XML 和 CSV 等其他文本格式。 最后,Web 服务器可以返回非文本数据,例如 PDF、压缩文件、音频和视频。
在本文中,除了我们刚刚返回的纯文本之外,您还将学习如何返回以下类型的数据:
- JSON
- CSV
- HTML
这三种数据类型都是基于文本的,并且是用于在 Web 上传递内容的流行格式。 许多服务器端开发语言和工具都支持返回这些不同的数据类型。 在 Node.js 的上下文中,我们需要做两件事:
- 使用适当的值在我们的 HTTP 响应中设置
Content-Type
标头。 - 确保
res.end()
以正确的格式获取数据。
让我们通过一些示例来看看这一点。 我们将在本节和后面编写的代码与我们之前编写的代码有很多相似之处。 大多数更改存在于 requestListener()
函数中。 让我们用这个“模板代码”创建文件,以使以后的部分更容易理解。
创建一个名为 html.js
的新文件。 该文件稍后将用于在 HTTP 响应中返回 HTML 文本。 我们将模板代码放在这里,并将其复制到其他返回各种类型的服务器。
在终端中,输入以下内容:
touch html.js
现在在文本编辑器中打开这个文件:
nano html.js
让我们复制“模板代码”。 在 nano
中输入:
第一服务器/html.js
const http = require("http"); const host = 'localhost'; const port = 8000; const requestListener = function (req, res) {}; const server = http.createServer(requestListener); server.listen(port, host, () => { console.log(`Server is running on http://${host}:${port}`); });
用CTRL+X
保存退出html.js
,然后返回终端。
现在让我们将此文件复制到两个新文件中。 第一个文件将在 HTTP 响应中返回 CSV 数据:
cp html.js csv.js
第二个文件将在服务器中返回 JSON 响应:
cp html.js json.js
剩下的文件将用于以后的练习:
cp html.js htmlFile.js cp html.js routes.js
我们现在准备继续我们的练习。 让我们从返回 JSON 开始。
服务 JSON
JavaScript Object Notation,通常称为JSON,是一种基于文本的数据交换格式。 顾名思义,它源自 JavaScript 对象,但它与语言无关,这意味着它可以被任何可以解析其语法的编程语言使用。
API 通常使用 JSON 来接受和返回数据。 它的流行是由于数据传输的大小比以前的数据交换标准(如 XML)要小,以及现有的工具允许程序在不费力的情况下解析它们。 如果您想了解有关 JSON 的更多信息,您可以阅读我们关于 如何在 JavaScript 中使用 JSON 的指南。
用 nano
打开 json.js
文件:
nano json.js
我们想要返回一个 JSON 响应。 让我们修改 requestListener()
函数以通过更改突出显示的行来返回所有 JSON 响应具有的适当标头,如下所示:
第一服务器/json.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); }; ...
res.setHeader()
方法将 HTTP 标头添加到响应中。 HTTP 标头是可以附加到请求或响应的附加信息。 res.setHeader()
方法有两个参数:标题的名称和它的值。
Content-Type
标头用于指示与请求或响应一起发送的数据格式,也称为媒体类型。 在这种情况下,我们的 Content-Type
是 application/json
。
现在,让我们将 JSON 内容返回给用户。 修改 json.js
使其看起来像这样:
第一服务器/json.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); res.writeHead(200); res.end(`{"message": "This is a JSON response"}`); }; ...
和之前一样,我们通过返回状态码 200
来告诉用户他们的请求成功。 这次在 response.end()
调用中,我们的字符串参数包含有效的 JSON。
按 CTRL+X
保存并退出 json.js
。 现在,让我们使用 node
命令运行服务器:
node json.js
在另一个终端中,让我们使用 cURL 访问服务器:
curl http://localhost:8000
当我们按下 ENTER
时,我们将看到以下结果:
Output{"message": "This is a JSON response"}
我们现在已经成功返回一个 JSON 响应,就像我们创建应用程序使用的许多流行 API 一样。 请务必使用 CTRL+C
退出正在运行的服务器,以便我们可以返回到标准终端提示符。 接下来,我们来看另一种流行的返回数据格式:CSV。
服务 CSV
逗号分隔值 (CSV) 文件格式是一种文本标准,通常用于提供表格数据。 在大多数情况下,每一行由换行符分隔,行中的每个项目由逗号分隔。
在我们的工作区中,使用文本编辑器打开 csv.js
文件:
nano csv.js
让我们将以下行添加到我们的 requestListener()
函数中:
第一服务器/csv.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/csv"); res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv"); }; ...
这一次,我们的 Content-Type
表示正在返回一个 CSV 文件,因为值为 text/csv
。 我们添加的第二个标题是 Content-Disposition
。 此标头告诉浏览器如何显示数据,特别是在浏览器中或作为单独的文件。
当我们返回 CSV 响应时,即使未设置 Content-Disposition
标头,大多数现代浏览器也会自动下载文件。 但是,当返回 CSV 文件时,我们仍应添加此标头,因为它允许我们设置 CSV 文件的名称。 在这种情况下,我们会向浏览器发出信号,告知该 CSV 文件是附件,应该下载。 然后我们告诉浏览器文件的名字是oceanpals.csv
。
让我们在 HTTP 响应中写入 CSV 数据:
第一服务器/csv.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/csv"); res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv"); res.writeHead(200); res.end(`id,name,email\n1,Sammy Shark,shark@ocean.com`); }; ...
就像我们在响应中返回 200
/OK
状态之前一样。 这一次,我们对 res.end()
的调用有一个有效的 CSV 字符串。 逗号分隔每列中的值,换行符 (\n
) 分隔行。 我们有两行,一行用于表头,一行用于数据。
我们将在浏览器中测试这个服务器。 保存 csv.js
并使用 CTRL+X
退出编辑器。
使用 Node.js 命令运行服务器:
node csv.js
在另一个终端中,让我们使用 cURL 访问服务器:
curl http://localhost:8000
控制台将显示:
Outputid,name,email 1,Sammy Shark,shark@ocean.com
如果我们在浏览器中转到 http://localhost:8000
,则会下载一个 CSV 文件。 其文件名为 oceanpals.csv
。
使用 CTRL+C
退出正在运行的服务器以返回标准终端提示符。
返回 JSON 和 CSV 后,我们介绍了两种流行的 API 案例。 让我们继续讨论我们如何为人们在浏览器中查看的网站返回数据。
提供 HTML
HTML,超文本标记语言,是我们希望用户通过网络浏览器与我们的服务器交互时最常用的格式。 创建它是为了构建 Web 内容。 Web 浏览器旨在显示 HTML 内容,以及我们使用 CSS 添加的任何样式,另一种前端 Web 技术允许我们改变网站的美学。
让我们用我们的文本编辑器重新打开 html.js
:
nano html.js
修改 requestListener()
函数为 HTML 响应返回适当的 Content-Type
标头:
第一服务器/html.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/html"); }; ...
现在,让我们将 HTML 内容返回给用户。 将突出显示的行添加到 html.js
,如下所示:
第一服务器/html.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(`<html><body><h1>This is HTML</h1></body></html>`); }; ...
我们首先添加 HTTP 状态码。 然后我们使用包含有效 HTML 的字符串参数调用 response.end()
。 当我们在浏览器中访问我们的服务器时,我们将看到一个带有一个标题标签的 HTML 页面,其中包含 This is HTML
。
让我们按CTRL+X
保存并退出。 现在,让我们使用 node
命令运行服务器:
node html.js
当我们的程序启动时,我们将看到 Server is running on http://localhost:8000
。
现在进入浏览器并访问http://localhost:8000
。 我们的页面将如下所示:
让我们用 CTRL+C
退出正在运行的服务器并返回标准终端提示符。
HTML 写在一个文件中是很常见的,与我们的 Node.js 程序等服务器端代码分开。 接下来,让我们看看如何从文件中返回 HTML 响应。
第 3 步 — 从文件中提供 HTML 页面
我们可以将 HTML 作为 Node.js 中的字符串提供给用户,但我们最好加载 HTML 文件并提供其内容。 这样,随着 HTML 文件的增长,我们不必在 Node.js 代码中维护长字符串,使其更加简洁,并允许我们独立处理网站的各个方面。 这种“关注点分离”在许多 Web 开发设置中很常见,因此最好知道如何在 Node.js 中加载 HTML 文件以支持它
为了提供 HTML 文件,我们使用 fs 模块 加载 HTML 文件,并在编写 HTTP 响应时使用它的数据。
首先,我们将创建一个 Web 服务器将返回的 HTML 文件。 创建一个新的 HTML 文件:
touch index.html
现在在文本编辑器中打开 index.html
:
nano index.html
我们的网页将是最小的。 它将具有橙色背景,并在中心显示一些问候语。 将此代码添加到文件中:
第一服务器/index.html
<!DOCTYPE html> <head> <title>My Website</title> <style> *, html { margin: 0; padding: 0; border: 0; } html { width: 100%; height: 100%; } body { width: 100%; height: 100%; position: relative; background-color: rgb(236, 152, 42); } .center { width: 100%; height: 50%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; font-family: "Trebuchet MS", Helvetica, sans-serif; text-align: center; } h1 { font-size: 144px; } p { font-size: 64px; } </style> </head> <body> <div class="center"> <h1>Hello Again!</h1> <p>This is served from a file</p> </div> </body> </html>
这个网页显示了两行文本:Hello Again!
和 This is served from a file
。 这些线条出现在页面的中心,一个在一个之上。 第一行文本显示在标题中,这意味着它会很大。 第二行文本会显得稍小一些。 所有文本将显示为白色,网页具有橙色背景。
虽然这不是本文或系列的范围,但如果您有兴趣了解有关 HTML、CSS 和其他前端 Web 技术的更多信息,可以查看 Mozilla 的 Web 入门 指南.
这就是我们需要的 HTML,所以使用 CTRL+X
保存并退出文件。 我们现在可以转到服务器代码。
对于本练习,我们将处理 htmlFile.js
。 用文本编辑器打开它:
nano htmlFile.js
由于我们必须读取一个文件,让我们从导入 fs
模块开始:
第一服务器/htmlFile.js
const http = require("http"); const fs = require('fs').promises; ...
这个模块包含一个 readFile()
函数,我们将使用它来加载 HTML 文件。 我们根据现代 JavaScript 最佳实践导入了 Promise 变体。 我们使用 Promise 作为它的语法比回调更简洁,如果我们将 fs
分配给 require('fs')
,我们将不得不使用它。 要了解有关异步编程最佳实践的更多信息,您可以阅读我们的 如何在 Node.js 中编写异步代码指南 。
我们希望在用户请求我们的系统时读取我们的 HTML 文件。 让我们从修改 requestListener()
来读取文件开始:
第一服务器/htmlFile.js
... const requestListener = function (req, res) { fs.readFile(__dirname + "/index.html") }; ...
我们使用 fs.readFile()
方法来加载文件。 它的参数有 __dirname + "/index.html"
。 特殊变量 __dirname 具有运行 Node.js 代码的绝对路径。 然后我们附加 /index.html
以便我们可以加载我们之前创建的 HTML 文件。
现在让我们在加载后返回 HTML 页面:
第一服务器/htmlFile.js
... const requestListener = function (req, res) { fs.readFile(__dirname + "/index.html") .then(contents => { res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(contents); }) }; ...
如果 fs.readFile()
承诺成功解决,它将返回其数据。 我们使用 then()
方法来处理这种情况。 contents
参数包含 HTML 文件的数据。
我们首先将 Content-Type
标头设置为 text/html
以告诉客户端我们正在返回 HTML 数据。 然后我们编写状态码以指示请求成功。 我们最终将我们加载的 HTML 页面发送给客户端,其中包含 contents
变量中的数据。
fs.readFile()
方法有时会失败,所以我们应该在遇到错误时处理这种情况。 将此添加到 requestListener()
函数中:
第一服务器/htmlFile.js
... const requestListener = function (req, res) { fs.readFile(__dirname + "/index.html") .then(contents => { res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(contents); }) .catch(err => { res.writeHead(500); res.end(err); return; }); }; ...
保存文件并使用 CTRL+X
退出 nano
。
当 Promise 遇到错误时,它会被拒绝。 我们使用 catch()
方法处理这种情况。 它接受 fs.readFile()
返回的错误,将状态码设置为 500
表示遇到内部错误,并将错误返回给用户。
使用 node
命令运行我们的服务器:
node htmlFile.js
在网络浏览器中,访问 http://localhost:8000
。 你会看到这个页面:
您现在已经从服务器返回了一个 HTML 页面给用户。 您可以使用 CTRL+C
退出正在运行的服务器。 当您这样做时,您将看到终端提示返回。
在生产环境中编写这样的代码时,您可能不希望每次收到 HTTP 请求时都加载 HTML 页面。 虽然这个 HTML 页面的大小大约为 800 字节,但更复杂的网站可能有兆字节大小。 大文件可能需要一段时间才能加载。 如果您的网站预计会有大量流量,最好在启动时加载 HTML 文件并保存其内容。 加载它们后,您可以设置服务器并使其侦听地址上的请求。
为了演示这种方法,让我们看看如何重新设计我们的服务器以提高效率和可扩展性。
高效地提供 HTML
在此步骤中,我们将在开始时加载一次,而不是为每个请求加载 HTML。 该请求将返回我们在启动时加载的数据。
在终端中,使用文本编辑器重新打开 Node.js 脚本:
nano htmlFile.js
让我们在创建 requestListener()
函数之前添加一个新变量:
第一服务器/htmlFile.js
... let indexFile; const requestListener = function (req, res) { ...
当我们运行这个程序时,这个变量将保存 HTML 文件的内容。
现在,让我们重新调整 requestListener()
功能。 它现在将返回 indexFile
的内容,而不是加载文件:
第一服务器/htmlFile.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(indexFile); }; ...
接下来,我们将文件读取逻辑从 requestListener()
函数转移到我们的服务器启动。 在我们创建服务器时进行以下更改:
第一服务器/htmlFile.js
... const server = http.createServer(requestListener); fs.readFile(__dirname + "/index.html") .then(contents => { indexFile = contents; server.listen(port, host, () => { console.log(`Server is running on http://${host}:${port}`); }); }) .catch(err => { console.error(`Could not read index.html file: ${err}`); process.exit(1); });
保存文件并使用 CTRL+X
退出 nano
。
读取文件的代码与我们第一次尝试编写的代码类似。 但是,当我们成功读取文件时,我们现在将内容保存到全局 indexFile
变量中。 然后我们使用 listen()
方法启动服务器。 关键是在服务器运行之前加载文件。 这样,requestListener()
函数肯定会返回一个 HTML 页面,因为 indexFile
不再是一个空变量。
我们的错误处理程序也发生了变化。 如果文件无法加载,我们会捕获错误并将其打印到控制台。 然后我们在不启动服务器的情况下使用 exit()
函数退出 Node.js 程序。 这样我们就可以看到文件读取失败的原因,解决问题,然后重新启动服务器。
我们现在创建了不同的 Web 服务器,它们向用户返回各种类型的数据。 到目前为止,我们还没有使用任何请求数据来确定应该返回什么。 在 Node.js 服务器中设置不同的路由或路径时,我们需要使用请求数据,接下来让我们看看它们是如何协同工作的。
第 4 步 — 使用 HTTP 请求对象管理路由
我们访问的大多数网站或我们使用的 API 通常都有多个端点,因此我们可以访问各种资源。 一个很好的例子是图书管理系统,它可以在图书馆中使用。 它不仅需要管理图书数据,还需要管理作者数据以方便编目和搜索。
尽管书籍和作者的数据是相关的,但它们是两个不同的对象。 在这些情况下,软件开发人员通常在不同的端点上对每个对象进行编码,以向 API 用户指示他们正在与哪种数据进行交互。
让我们为一个小型库创建一个新服务器,它将返回两种不同类型的数据。 如果用户访问我们的服务器地址 /books
,他们将收到 JSON 格式的图书列表。 如果他们转到 /authors
,他们将收到 JSON 格式的作者信息列表。
到目前为止,我们对收到的每个请求都返回了相同的响应。 让我们快速说明这一点。
重新运行我们的 JSON 响应示例:
node json.js
在另一个终端中,让我们像以前一样执行 cURL 请求:
curl http://localhost:8000
你会看见:
Output{"message": "This is a JSON response"}
现在让我们尝试另一个 curl 命令:
curl http://localhost:8000/todos
按下 Enter
后,您将看到相同的结果:
Output{"message": "This is a JSON response"}
我们没有在 requestListener()
函数中构建任何特殊逻辑来处理 URL 包含 /todos
的请求,因此 Node.js 默认返回相同的 JSON 消息。
由于我们想要构建一个微型图书馆管理服务器,我们现在将根据用户访问的端点分离返回的数据类型。
首先,使用 CTRL+C
退出正在运行的服务器。
现在在您的文本编辑器中打开 routes.js
:
nano routes.js
让我们首先将 JSON 数据存储在 requestListener()
函数之前的变量中:
第一服务器/routes.js
... const books = JSON.stringify([ { title: "The Alchemist", author: "Paulo Coelho", year: 1988 }, { title: "The Prophet", author: "Kahlil Gibran", year: 1923 } ]); const authors = JSON.stringify([ { name: "Paulo Coelho", countryOfBirth: "Brazil", yearOfBirth: 1947 }, { name: "Kahlil Gibran", countryOfBirth: "Lebanon", yearOfBirth: 1883 } ]); ...
books
变量是一个字符串,其中包含用于书籍对象数组的 JSON。 每本书都有标题或名称、作者和出版年份。
authors
变量是一个字符串,其中包含一组作者对象的 JSON。 每个作者都有姓名、出生国家和出生年份。
现在我们有了响应将返回的数据,让我们开始修改 requestListener()
函数以将它们返回到正确的路由。
首先,我们将确保来自我们服务器的每个响应都有正确的 Content-Type
标头:
第一服务器/routes.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); } ...
现在,我们希望根据用户访问的 URL 路径返回正确的 JSON。 让我们在请求的 URL 上创建一个 switch 语句:
第一服务器/routes.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); switch (req.url) {} } ...
要从请求对象中获取 URL 路径,我们需要访问其 url
属性。 我们现在可以将案例添加到 switch
语句以返回适当的 JSON。
JavaScript 的 switch
语句提供了一种根据对象或 JavaScript 表达式的值(例如,数学运算的结果)来控制运行哪些代码的方法。 如果您需要有关如何使用它们的课程或提醒,请查看我们的 如何在 JavaScript 中使用 Switch 语句的指南。
让我们继续添加 case
当用户想要获取我们的书籍列表时:
第一服务器/routes.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); switch (req.url) { case "/books": res.writeHead(200); res.end(books); break } } ...
我们将状态码设置为 200
以指示请求正常并返回包含我们图书列表的 JSON。 现在让我们为我们的作者添加另一个 case
:
第一服务器/routes.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); switch (req.url) { case "/books": res.writeHead(200); res.end(books); break case "/authors": res.writeHead(200); res.end(authors); break } } ...
和以前一样,状态码将是 200
,因为请求很好。 这次我们返回包含作者列表的 JSON。
如果用户尝试转到任何其他路径,我们希望返回错误。 让我们添加默认情况来执行此操作:
路由.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); switch (req.url) { case "/books": res.writeHead(200); res.end(books); break case "/authors": res.writeHead(200); res.end(authors); break default: res.writeHead(404); res.end(JSON.stringify({error:"Resource not found"})); } } ...
我们在 switch
语句中使用 default
关键字来捕获我们之前的案例未捕获的所有其他场景。 我们将状态码设置为 404
表示他们正在寻找的 URL 没有找到。 然后我们设置一个包含错误消息的 JSON 对象。
让我们测试一下我们的服务器,看看它的行为是否符合我们的预期。 在另一个终端中,让我们首先运行一个命令,看看我们是否能取回我们的书籍列表:
curl http://localhost:8000/books
按 Enter
可以看到如下输出:
Output[{"title":"The Alchemist","author":"Paulo Coelho","year":1988},{"title":"The Prophet","author":"Kahlil Gibran","year":1923}]
到现在为止还挺好。 让我们对 /authors
进行相同的尝试。 在终端中键入以下命令:
curl http://localhost:8000/authors
命令完成后,您将看到以下输出:
Output[{"name":"Paulo Coelho","countryOfBirth":"Brazil","yearOfBirth":1947},{"name":"Kahlil Gibran","countryOfBirth":"Lebanon","yearOfBirth":1883}]
最后,让我们尝试一个错误的 URL,以确保 requestListener()
返回错误响应:
curl http://localhost:8000/notreal
输入该命令将显示此消息:
Output{"error":"Resource not found"}
您可以使用 CTRL+C
退出正在运行的服务器。
我们现在为用户创建了不同的途径来获取不同的数据。 我们还添加了一个默认响应,如果用户输入我们不支持的 URL,该响应将返回 HTTP 错误。
结论
在本教程中,您制作了一系列 Node.js HTTP 服务器。 您首先返回了一个基本的文本响应。 然后,您继续从我们的服务器返回各种类型的数据:JSON、CSV 和 HTML。 从那里,您可以将文件加载与 HTTP 响应结合起来,从服务器返回一个 HTML 页面给用户,并创建一个 API,该 API 使用有关用户请求的信息来确定应在其响应中发送哪些数据。
您现在可以创建可以处理各种请求和响应的 Web 服务器。 有了这些知识,您就可以创建一个服务器,在不同的端点向用户返回许多 HTML 页面。 您还可以创建自己的 API。
要了解更多 Node.js 中的 HTTP Web 服务器,您可以阅读 http
模块上的 Node.js 文档。 如果您想继续学习 Node.js,可以返回 如何在 Node.js 中编写代码系列页面。