写点什么

调用 Express API 时出现奇怪的 CORS 错误该怎么办?

  • 2020-12-18
  • 本文字数:3288 字

    阅读完需:约 11 分钟

调用 Express API时出现奇怪的CORS错误该怎么办?

想象一下这样一个场景:你用 Express 创建了一个 API,然后给前端加了一些 JavaScript 代码,这些 JS 代码会向这个 API 发送请求。事情本来很顺利,然后你在浏览器中加载了前端,突然在控制台中看到了一个奇怪的错误:


Access to fetch at 'https://your-api.com/user/1234' from origin 'https://your-website.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
复制代码


然后你可能会试着按照错误消息的提示,将请求的模式设置为 no-cors,但是对 API 的请求还是跑不通。就算你在网上搜索一番,也很难搞清楚为什么会发生这种事情,该怎么才能解决它。


好消息是,有一个 Express 库可用来修复这些 CORS 错误,但在我们着手解决这些错误之前,我们是不是该看看它们到底是什么意思?为了理解这些错误,我们先来看一下 CORS 究竟是什么。


什么是 CORS?为什么它会毁掉你美好的一天?


CORS 是跨域资源共享(Cross-Origin Resource Sharing)的简写,并且所有现代浏览器都支持它。这个概念有点拗口,所以我们把它拆开来细看,才能了解它到底做的是什么事情。


什么是“资源”?


资源是可在特定 URL 获取的内容,如 HTML 网页、图像或 JSON API 响应。实际上,它是构成万维网的“砖块”。


什么是“来源”?


一份资源的来源(origin)是协议+域+端口,例如网址https://your-api.com:8080/user/1234的源是https://your-api.com:8080。如果网址不包含端口,则源就只有协议+域。


跨域资源共享到底是做什么的?


当 Web 浏览器需要确保网站的前端 JavaScript(来源 A)仅在另一来源(来源 B)明确允许的情况下才可以访问 B 的资源时,就要用到跨域资源共享。如果来源 B 确实给出了许可,那么资源就会跨域共享!


CORS 可以帮助阻止恶意网站访问和使用敏感数据。当你在浏览器中看到那些烦人的 CORS 错误时,实际上是你的 Web 浏览器正在尽最大努力保护你免受被识别为潜在恶意请求的威胁。


CORS 如何工作?


Web 浏览器决定是否允许跨域共享资源时,会在前端 JavaScript 发出的请求上设置 Origin 标头。然后,浏览器检查在资源响应上设置的 CORS 标头。它在响应上检查的标头取决于浏览器发出的请求类型,但响应至少必须将 Access-Control-Allow-Origin 标头设置为一个受许可的值,才能让 Web 浏览器对发起请求的前端 JavaScript 给出响应。


CORS 请求示例


跨域请求的一个示例是,从网页上的前端 JavaScript(托管在一个域「来源 A」上)发起一个 GET 请求,到你托管在另一个域(来源 B)的 API 端点上。


浏览器通过https://your-website.com/user-profile网页上的 JavaScript 发出的请求将包含以下信息:


> GET /user/1234 HTTP/1.1> Host: your-api.com> Origin: https://your-website.com
复制代码


Origin 请求标头是由 Web 浏览器自动设置的——出于安全原因,通过 fetch 请求时无法设置其值。为了让上面的示例 CORS 请求正确运行,你的 API 发出的响应应该是这个样子:


< HTTP/1.1 200 OK< Access-Control-Allow-Origin: https://your-website.com< Vary: Origin< Content-Type: application/json; charset=utf-8< {"name":"Existing Person"}
复制代码


请注意,Access-Control-Allow-Origin 响应标头的值与 Origin 响应的值是呼应的:https://your-website.com。Web 浏览器会看到这个 CORS 响应标头,并确认它具有与网页上的前端 JavaScript 共享响应内容的权限。现在我们对 CORS 的概念和作用有了更好的了解,是时候设置一些 CORS 标头并修复你在网页上遇到的错误了。


如何设置 CORS 标头并摆脱那些烦人的错误


如你在上面的示例中所看到的,对于 Web 浏览器来说,在对 API 的请求中发送 Origin 标头是很重要的,但在响应中发送所有重要的 Access-Control-*标头的是你的 API。这些 CORS 标头会告诉 Web 浏览器是否允许将来自 API 的响应提供给前端 JavaScript。


为了帮助你解决你烦人的 CORS 错误,你需要用到 cors middleware package 这个库。在终端中转到包含 Express 应用程序的目录,然后安装它:


npm install cors
复制代码


注意:在这篇博客文章中,我会链接到 GitHub(而非 npm)上的 cors 软件包,因为在撰写本文时,npm 上的这个包的文档已经过时了。


安装完成后,你需要在应用程序中 require 它(require express 之后直接处理它即可):


const cors = require("cors");
复制代码


如果在不传递任何配置选项的情况下在 Express 应用程序中调用 cors 中间件,则默认情况下,它将在 API 响应中添加 CORS 响应标头 Access-Control-Allow-Origin: *。这意味着任何来源(即任何域上的网页)都可以向你的 API 发出请求。除非你要构建供公众使用的 API,否则你肯定不希望这么做。因此我们需要配置 cors 中间件,配置成只有你的网站才能向你的 API 发出 CORS 请求:


/** * These options will be used to configure the cors middleware to add * these headers to the response: *  * Access-Control-Allow-Origin: https://your-website.com * Vary: Origin */const corsOptions = {  origin: "https://your-website.com"};/** * This configures all of the following routes to use the cors middleware * with the options defined above. */app.use(cors(corsOptions));app.get("/user/:id", (request, response) => {  response.json({ name: "Existing Person" });});app.get("/country/:id", (request, response) => {  response.json({ name: "Oceania" });});app.get("/address/:id", (request, response) => {  response.json({ street: "Gresham Lane", city: "Lakeville" });});
复制代码


一般来说,你需要像上面的示例一样为 Express 应用程序中的所有路由启用 CORS,但是如果你只想为特定路由启用 CORS,则可以按以下方式配置 cors 中间件:


/** * These options will be used to configure the cors middleware to add * these headers to the response: *  * Access-Control-Allow-Origin: https://your-website.com * Vary: Origin */const corsOptions = {  origin: "https://your-website.com"};// This route is using the cors middleware with the options defined above.app.get("/user/:id", cors(corsOptions), (request, response) => {  response.json({ name: "Existing Person" });});// This route is using the cors middleware with the options defined above.app.get("/country/:id", cors(corsOptions), (request, response) => {  response.json({ name: "Oceania" });});/** * We never want this API route to be requested from a browser, * so we don't configure the route to use the cors middleware. */app.get("/address/:id", (request, response) => {  response.json({ street: "Gresham Lane", city: "Lakeville" });});
复制代码


启用“复杂”CORS 请求


上面的示例是为简单的 GET 请求配置 CORS 的。对于其他许多类型的 CORS 请求,Web 浏览器将在发出实际 CORS 请求之前先发出 CORS“预检”(pre-flight)请求。这个预请求使用了 OPTIONS HTTP 方法,它可以帮助浏览器确定是否允许发出 CORS 请求。


cors 中间件提供了启用 CORS 预检的说明,并允许你配置在对预检请求的响应中发送的标头。


不用再担心 COR 了 S


希望本文能够帮助你全面了解 CORS,但有时我们确实很难弄清楚如何配置才能让 CORS 请求正常工作。下面列举一些有用的资源:



祝大家跨域资源共享愉快!


原文链接:How To Fix Those Confusing Cors Errors When Calling Your Express API

2020-12-18 11:552427

评论

发布
暂无评论
发现更多内容

手势识别技术及其应用

数据堂

基于STM32设计的智能灌溉控制系统

DS小龙哥

6 月 优质更文活动

对线面试官-Sychronized和ReentrantLock

派大星

Java 面试题

精打细算:OptaPlanner如何帮助您找到最佳优惠券组合

PeterOne

算法 运筹优化 6 月 优质更文活动

Generative AI 新世界:文本生成领域论文解读

亚马逊云科技 (Amazon Web Services)

机器学习

中国科学院院士梅宏:推进构建面向全球的开源生态治理体系

开放原子开源基金会

开源 开放原子 梅宏

加速44%!RT-DETR量化无损压缩优秀实战

飞桨PaddlePaddle

人工智能 百度 paddle

Nautilus Chain测试网迎阶段性里程碑,模块化区块链拉开新序幕

BlockChain先知

OneCode-流程引擎设计

codebee

Nautilus Chain测试网迎阶段性里程碑,模块化区块链拉开新序幕

股市老人

生产环境可用的 Seata-go 1.2.0 来啦!!!

SOFAStack

Java seata 版本发布 Seata框架 Seata-go

当 GraphQL 遇上图数据库,便有了更方便查询数据的方式

NebulaGraph

图数据库 graphql

智达方通EPM 发布5.0版本,持续赋能企业数智化管理

智达方通

EPM 智达方通 全面预算管理 智达方通EPM 企业绩效管理系统

用Optaplanner实现云资源优化:建模详解

PeterOne

算法 后端 运筹优化 6 月 优质更文活动

以商业实践反哺开源技术跃升|2023开放原子全球开源峰会开源商业化创新发展分论坛成功举行

开放原子开源基金会

开源 开源商业化 开放原子全球开源峰会

TS接口类型

不叫猫先生

6 月 优质更文活动

GitHub 2800颗星,支持GPT/Transformer,字节跳动这个开源项目是怎么来的?

字节跳动技术范儿

机器学习 nlp

架构模块2作业

sandywrh

ubuntu服务器安装图形化界面总结

Studying_swz

6 月 优质更文活动

Spring Security6 全新写法,大变样!

江南一点雨

Java springboot springsecurity

Seata Saga 模式快速入门和最佳实践

SOFAStack

分布式 云原生 开发 seata Seata框架

JS中, Set为什么是带键的集合?

林十二XII

前端

2023-06-14:我们从二叉树的根节点 root 开始进行深度优先搜索。 在遍历中的每个节点处,我们输出 D 条短划线(其中 D 是该节点的深度) 然后输出该节点的值。(如果节点的深度为 D,则其

福大大架构师每日一题

Go 算法 rust 福大大架构师每日一题

C语言编程—命令行参数

芯动大师

C语言 命令行参数解析 6 月 优质更文活动

手势识别技术的挑战和未来发展

数据堂

海纳 “千川”:得物多场景统一推荐平台

得物技术

WMS 库存系统设计

红袖添香

系统设计 供应链物流 WMS系统 库存系统

深入理解Pod的核心知识

穿过生命散发芬芳

pod 6 月 优质更文活动

MySQL8新特性窗口函数详解

越长大越悲伤

MySQL sql

调用 Express API时出现奇怪的CORS错误该怎么办?_语言 & 开发_Simon Plenderleith_InfoQ精选文章