Node.js 中 CORS 的使用

2025年2月25日 | 10分钟阅读

在 Node.js 中,CORS 代表跨域资源共享(Cross-Origin Resource Sharing)。前端客户端可以使用 JavaScript 方法从外部后端服务器请求资源。同源策略禁止跨域请求,而需要使用 CORS 头部来禁用此功能。本文将教我们如何配置一个 ExpressJS 后端服务器,并使用一个前端向其提交跨域请求。

先决条件

  • 对 RESTful API 有初步了解。
  • 对 ExpressJS 有基本了解。
  • 理解请求和响应,以及客户端-服务器模型。
  • 对中间件有基本了解。

什么是 CORS?

CORS 的缩写是跨域资源共享(Cross-Origin Resource Sharing)。每个 Web 应用程序都有一个前端和一个后端,它们都使用 API 进行相互通信。前端和后端通常托管在同一个源上。Node.js 的 CORS 有助于从外部服务检索资源。下面的类比可以帮助解释这一点。

以一个餐厅应用为例。源就是我们餐厅的地址。用餐区是前端,厨房是后端。厨房和用餐区位于同一地址。因此,它们可以交换食物和服务,或者在我们的例子中是资源。顾客可能会感到不适并需要药物,而这些药物需要从餐厅外的其他地方带来。从技术上讲,前端请求的后端不是它自己的源。因此,跨域资源共享是前端从一个独立的后端请求额外服务或资源的过程。

通常,应用程序的前端只能使用其自己的源向后端进行 API 调用。从安全角度来看,这被称为同源策略,它非常重要。浏览器将拒绝来自或去往不同源前端的任何请求。当出现需要访问第三方资源的情况时,CORS 使我们能够绕过这一策略。

Use of CORS in Node.js

注意:浏览器可以在同源策略下发出请求,但响应将被禁止。

CORS 是如何工作的?

正如我们所讨论的,浏览器实施了同源策略,允许从其自己的 URL 访问资源,但阻止从外部 URL 检索资源。Node.js 中的 CORS 可以改变这一点。浏览器发送的请求消息中可能包含一个 Origin 头部。如果请求发送到同源服务器,浏览器会接受该请求并且不会被阻止。另一方面,如果请求发送到非源服务器,则该请求被称为跨域请求。服务器在发送响应时,会在响应中包含 Access-Control-Allow-Origin 头部。此头部的值必须与请求的 Origin 头部的值一致。如果匹配,则请求被批准。如果它们不匹配,浏览器将阻止响应数据。此时,用户的浏览器控制台中将出现臭名昭著的 CORS 错误。

在进一步深入之前,让我们先探讨一下 Node.js 中 Cors 的源(Origin)。正如我们之前提到的,源的改变将阻止所有请求,直到 CORS 被实现。

源是一个请求头部,它标识了发出请求的客户端的 URL。它分为三个部分:

  1. 协议,如 HTTP 和 HTTPS。
  2. 域名,如 www.corsinnodejs.com 或 localhost。
  3. 端口,例如 8080、8888。

这三者结合的结果如下:


Use of CORS in Node.js

对上述三个组件的任何更改都可能导致 cors 错误和新的源 URL。为了防止资源被阻止,我们必须确保请求和响应的源匹配,或者在 Node.js 中配置了 CORS。

现在让我们来看看头部(headers)。简单来说,头部用于随请求或响应传递额外的数据。浏览器或服务器需要这些数据来执行多项任务。请求头提供了有关请求的附加细节,而响应头则提供了有关响应的附加细节。

  • Origin: 此头部显示请求和响应的 URL。
  • Access-Control-Allow-Origin: 此头部提供可用于从不同源请求内容的 URL。

示例

让我们看一个 YouTube 克隆应用的例子,来更多地了解 Node.js 中的 cors。

  • 这是服务器的通用请求。我们正在尝试通过发送 GET 请求来检索 50 个新视频。
  • 这些是请求的头部。协议(scheme)、源(origin)、路径(path)、键(key)等的存在应该是显而易见的。
  • 这些是响应的头部。请注意其方法、内容类型和 access-control-allow-origin 属性。它表示浏览器已经批准了在“access-control-allow-origin”头部中指定的 URL。{*} 表示所有客户端源都是可接受的。
  • 所以,我们可以看到请求的头部包含了源 URL。响应的头部包含了 access-control-allow-origin URL(或在本例中为 *,它允许所有源 URL)。前端客户端向后端发出请求,由于后端允许来自任何源 URL 的请求,浏览器批准了该请求,客户端收到了数据。请求成功完成,状态码 200 证明了这一点。
  • 在下一节中,我们将从头开始构建一个简单的应用程序,并研究当我们尝试在没有 CORS 头部的情况下访问外部后端时会发生什么。
Use of CORS in Node.js

在 Node.JS 中的一个基本 CORS 演示(错误与解决方案示例)

当 CORS 启用时,已经可以观察到跨域资源获取成功工作。现在,让我们看看当在未启用 CORS 的情况下发出跨域请求时会发生什么。我们将创建一个简单的 application.js 来演示 node 中的 cors。

步骤 1

首先,请确认我们的计算机上已安装 npm 和 node.js。启动终端并输入以下命令以检查其是否工作:

如果尚未安装,请访问 Node.JS 网站下载并安装该库。

步骤 2

在这一步,我们将组织我们的项目。该命令为我们创建了一个新目录。

我们使用该命令进入这个新目录。

我们使用该命令初始化了一个新的 NPM 项目。

我们可以在终端中输入所需的值。

Use of CORS in Node.js

现在,为了创建我们的服务器,我们将使用 npm 安装 ExpressJS。我们执行以下命令:

安装一个 IDE 扩展将使我们能够在服务器上运行我们的 HTML 页面。一个 IDE 插件可以模拟在服务器上托管前端客户端。我们的 IDE 是 VSCode,我们将使用的插件是 Live Server。

步骤 3

现在,让我们来编写服务器代码。我们的服务器将使用 ExpressJS 包创建,应用程序将监听一些端口。我们将建立路径 '/data',一个 "GET" 请求将返回 JSON 数据。这部分代码如下:

步骤 4

现在,让我们开始编写前端页面。一个简单的 HTML 页面使用 JavaScript 脚本向我们的后端服务器发起一个 fetch 请求。这部分代码如下:

我们的文件夹结构如下所示:

Use of CORS in Node.js

步骤 5

  • 现在我们的前端和后端服务器都将开始运行。
  • 在终端中打开服务器目录,然后输入以下命令来访问后端:

在我们的例子中,它将是:

这将启动我们的后端服务器。

Use of CORS in Node.js

现在,导航到前端的 HTML 页面,然后右键单击并选择“使用 Live Server 打开”。

Use of CORS in Node.js

步骤 6

现在,在浏览器中转到 HTML 页面来打开控制台。右键单击浏览器窗口选择“检查”。接下来,移动到“控制台”面板。这里将看到 CORS 错误。

Use of CORS in Node.js

正如错误消息中所述,由于前端和后端的源不同,浏览器拒绝了该请求。之后,浏览器显示了 CORS 错误。

现在,我们将使用 CORS 来解决这个问题。我们将使用 NPM 的 CORS 包。可以使用以下命令在 node.js 中安装 cors:

步骤 7

在这一步,我们在服务器上使用一个中间件,该中间件使用客户端的源 URL。我们创建一个对象并将其通过中间件传递,使用 origin 作为键,URL 作为值。这部分代码如下:

在这里,我们可以观察到中间件包含了客户端的源 URL。之前的错误消息中也有这个 URL。现在,我们将尝试重新启动我们的前端和后端服务器。根据控制台的检查,我们最终得到了想要的数据。因此,我们已经在 node.js 中使用 cors 成功下载了跨域资源。

Use of CORS in Node.js

启用所有 CORS 请求

在上一节中,我们为特定的源使用了 cors 中间件。这只会允许来自该特定 URL 的不同源之间的资源共享。如果我们想在 node.js 中允许所有 CORS 请求,我们可以将我们的中间件更改为以下内容:

这条语句必须写在我们的路由之前。

为单个路由启用 CORS

在上面的方法中,使用 app.use() 为所有路由启用了中间件。如果我们想在 node.js 中对特定路由使用 CORS,我们可以将我们的路由更改为如下所示:

正如我们在上一节中看到的,我们只为两个路由中的一个添加了 cors。

正如我们所见,在上面的代码片段中,我们只为两个路由中的一个添加了 cors。

使用选项配置 CORS

正如我们之前所见,cors() 函数接受一个带有 origin 的对象。这个对象也可以显式声明除 origin 之外的其他属性。举个例子,考虑这个:

在这里,请求方法和源 URL 都已提供。只有上述方法会被批准。在这种情况下,如果客户端提交一个 patch 或 delete 请求,它将会失败。他们将被允许发出 put 或 get 请求。

使用函数配置动态 CORS 源

另一个选择是尝试动态源配置。根据我们的需要,可以采取多种方法来实现这一点。在下面的部分中,我们构建了一个从数据库异步获取源 URL 的例子。

在这种情况下,我们根据请求头从数据库中检索我们的源。之后,options 对象将此源分配给我们的 Origin。

从数据源加载允许的源列表

服务器也可以为我们提供允许的源列表。其代码片段如下:

这将加载数据库中允许的源(db 对象)。如果没有找到源或者访问被拒绝,则会显示一个错误。

预检请求

每个请求都必须存在一些必需的特性,例如内容类型或请求方法。这些细节有时在请求头中是缺失的。在这些情况下,浏览器会利用其可用的信息,通过一个预检请求向后端服务器询问它是否会完成这些请求。可以将这个预检请求看作是一个测试,以确定我们是否会收到对后续请求的响应。虽然这最初看起来像是一种低效的方法,但这个响应可以在预定的时间内被缓存。我们可以使用这个示例来使用一个预检请求:

结论

  • CORS 本身就在 node 中。跨域资源共享(Cross-Origin Resource Sharing),或称 JS,是一种可以将资源分散到不同服务器上的机制。
  • 同源策略会阻止外部请求,即使它们对安全至关重要。我们需要 CORS 来绕过此功能。
  • 源(origin)是托管前端或后端服务器的 URL。
  • ExpressJS 可用于设置后端服务器。之后,我们可以将 CORS 作为中间件来使用。
  • 如果缺少 CORS 头部,对外部源的 fetch 请求将不被允许。
  • 根据我们的需求,有多种方法可以实现 CORS 请求和头部。
  • 发送预检请求是为了查明这些请求头是否会收到响应。