使用Axios时遇到了Access Control Origin Header错误

139

我正在使用Axios在React Web应用程序中进行API调用。但是,在Chrome中出现了以下错误:

XMLHttpRequest cannot load
https://example.restdb.io/rest/mock-data. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:8080' is therefore not allowed
access. 
{
    axios
      .get("https://example.restdb.io/rest/mock-data", {
        headers: {
          "x-apikey": "API_KEY",
        },
        responseType: "json",
      })
      .then((response) => {
        this.setState({ tableData: response.data });
      });
}

我也在Stack Overflow上阅读了一些关于同一个问题的答案,标题为Access-Control-Allow-Origin,但仍然无法解决这个问题。我不想在Chrome中使用扩展程序或使用临时解决方法来解决此问题。请建议解决上述问题的标准方法。

尝试了几个答案后,我尝试了这个方法:

headers: { 
  'x-apikey': '59a7ad19f5a9fa0808f11931',
  'Access-Control-Allow-Origin' : '*',
  'Access-Control-Allow-Methods':'GET,PUT,POST,DELETE,PATCH,OPTIONS',
},
现在我得到了以下错误信息,
Request header field Access-Control-Allow-Origin is not
allowed by Access-Control-Allow-Headers in preflight response 

1
检查您收到的响应中的HTTP响应代码。您是否从中获得了200 OK?因为当我查看它时,我看到的是503“服务不可用”。尝试直接浏览https://example.restdb.io/rest/mock-data,我认为至少在那里您会看到相同的情况。因此,如果服务器以503响应,则您的请求无法正常工作。我认为您收到CORS错误消息的唯一原因只是因为许多服务器通常不会在5xx响应或其他响应中发送Access-Control-Allow-Origin。他们只会在成功响应(例如,200 OK)中发送它。 - sideshowbarker
7
不要在请求中添加Access-Control-Allow-Origin。这个头部分严格来说只是服务器在响应中发送回给你的响应头部。将它添加到请求中唯一的影响就是会破坏事情。同样适用于Access-Control-Allow-Methods头部。将它们添加到请求中永远不会防止浏览器遇到问题,这是问题中提到的第一个错误。 - sideshowbarker
20个回答

187
我来试试这个复杂的主题。
什么是“来源”?
来源本身是一个主机的名称(方案、主机名和端口),例如https://www.google.com,或者可以是一个本地打开的文件file://等等。它是某物(例如一个网页)的起源所在。当你打开你的网络浏览器并访问https://www.google.com时,显示给你的网页的来源就是https://www.google.com。你可以在Chrome开发者工具的“安全”选项下看到这一点。

如果您通过文件浏览器打开本地HTML文件(而不是通过服务器提供服务),同样适用。

这与CORS问题有什么关系?
当你打开浏览器并访问https://website.example时,该网站的来源将是https://website.example。这个网站很可能只会获取图片、图标、js文件,并向https://website.example发起API调用,基本上它是从同一个服务器获取的。它正在对同一来源进行调用。
如果你打开浏览器并打开一个本地HTML文件,在该HTML文件中有JavaScript想要向Google发起请求,你会得到以下错误:

同源策略告诉浏览器阻止跨域请求。在这个例子中,源为null的请求试图访问https://www.google.com(一个跨域请求)。浏览器不允许这样做,因为CORS策略设置了不允许跨域请求。
如果我的页面是从本地服务器提供的,同样的规则也适用。

本地服务器示例

如果我们使用以下代码在 localhost:3000 上托管我们自己的本地 API 服务器:

const express = require('express')
const app = express()

app.use(express.static('public'))

app.get('/hello', function (req, res) {
    // res.header("Access-Control-Allow-Origin", "*");
    res.send('Hello World');
})

app.listen(3000, () => {
    console.log('alive');
})

打开一个HTML文件(该文件向localhost:3000服务器发出请求),从文件浏览器中打开该文件夹时,会出现以下错误:

由于网页不是从本地服务器 localhost:3000 上提供的,并且通过文件浏览器访问的源与服务器 API 源不同,因此正在尝试进行跨源请求。浏览器由于 CORS 策略而停止此尝试。
但是,如果我们取消注释掉的那一行:
const express = require('express')
const app = express()

app.use(express.static('public'))

app.get('/hello', function (req, res) {
    res.header("Access-Control-Allow-Origin", "*");
    res.send('Hello World');
})

app.listen(3000, () => {
    console.log('alive');
})

现在再试一次:

它能够工作,是因为发送HTTP响应的服务器现在包含了一个头部,声明允许跨域请求发送到服务器,这意味着浏览器会允许这种情况发生,因此不会出现错误。

只是为了明确,CORS策略是现代浏览器的安全功能,用于保护人们免受有害和恶意代码的侵害。

如何修复问题(以下其中一种方法)

  • 将页面从与所进行请求的源相同的地方提供(同一主机)。
  • 通过在响应头中明确声明允许接收跨域请求来允许服务器。
  • 如果使用反向代理(如Nginx),请配置Nginx发送允许CORS的响应头。
  • 不要使用浏览器。例如,使用cURL,它不关心浏览器那样的CORS策略,可以得到您想要的结果。

示例流程

以下内容摘自:跨域资源共享(CORS)

记住,同源策略告诉浏览器阻止跨域请求。当你想从不同的源获取公共资源时,提供资源的服务器需要告诉浏览器“这个请求来自的源可以访问我的资源”。浏览器会记住这个信息并允许跨域资源共享。
步骤1:客户端(浏览器)请求 当浏览器进行跨域请求时,浏览器会添加一个包含当前源(协议、主机和端口)的 Origin 头。
步骤2:服务器响应 在服务器端,当服务器看到这个头,并希望允许访问时,需要在响应中添加一个 Access-Control-Allow-Origin 头,指定请求的源(或 * 允许任何源)。
步骤3:浏览器接收响应 当浏览器看到带有适当的 Access-Control-Allow-Origin 头的响应时,浏览器允许共享响应数据给客户端站点。
更多链接
这里有另一个很好的答案,更详细地解释了发生的情况:https://dev59.com/FWgv5IYBdhLWcg3wY_42#10636765

20
这个回答非常全面,应该可以作为一个很好的参考。 - SeaWarrior404

47
如果您的后端支持CORS,您可能需要在请求中添加此标头:
headers: {"Access-Control-Allow-Origin": "*"}

[更新] Access-Control-Allow-Origin 是一个响应头 - 所以为了启用CORS,您需要将此标头添加到从服务器返回的响应中。

但对于大多数情况而言,更好的解决方案是配置反向代理,这样您的服务器就能够重定向来自前端的请求到后端,而无需启用CORS。

您可以在这里找到有关CORS机制的文档:https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS


2
我已经添加了以下内容: 'Access-Control-Allow-Origin' : '*', 'Access-Control-Allow-Methods' : 'GET,PUT,POST,DELETE,PATCH,OPTIONS',然而,现在我遇到了错误:“请求头字段Access-Control-Allow-Origin被预检响应Access-Control-Allow-Headers不允许。” 我正在使用restdb.io作为我的API终点。 - SeaWarrior404
你是否像这个例子中那样设置了CORS?https://restdb.io/docs/apikeys-and-cors - Vlad Povalii
37
"Access-Control-Allow-Origin" 是一个响应头,因此将其添加到请求中并没有任何作用。 - T.Chmelevskij

15

1
withCredentials: true解决了我的问题..但我并没有在req header中设置'Access-Control-Allow-Credentials':true..相反,我在服务器端的web.config中添加了这个并解决了问题。 - hightides1973

8
对于 Spring Boot - React js 应用程序,我在控制器上添加了 @CrossOrigin 注解,它可以正常工作。
@CrossOrigin(origins = {"http://localhost:3000"})
@RestController
@RequestMapping("/api")

但要注意添加正确的本地主机地址 => 'http://localhost:3000',不要在末尾添加 '/' => 'http://localhost:3000/',这是我的问题所在。


8

我遇到了同样的错误。我通过使用 npm i cors 在后端安装CORS来解决它。然后,您需要将以下代码添加到您的代码中:

const cors = require('cors');
app.use(cors());

这对我很有帮助;现在我可以使用AJAX发布我的表单,而无需添加任何自定义标头。

1
这对我有效,还要注意,在请求之前必须使用 app.use(cors()) - Tran Minh Tri

5

对于使用过 cors 包的任何人,需要更改如下内容:

 const cors = require('cors');    
 app.use(cors());

to

const cors = require('cors');  
app.use(cors({credentials: true, origin: 'http://localhost:5003'}));

http://localhost:5003更改为您的客户端域名


4
首先,CORS绝对是服务器端的问题,而不是客户端的问题。但在我的情况下,我非常确定服务器代码是正确的,因为其他应用程序在不同域上使用相同的服务器时都能正常工作。其他答案中更详细地描述了此解决方案。
我的问题始于我开始使用自定义实例的axios。在我的情况下,当我们在axios实例中使用baseURL然后尝试从任何地方进行GET或POST调用时,axios会在baseURL和请求URL之间添加一个斜杠/,这也是有意义的,但它是隐藏的问题。我的Laravel服务器正在重定向以删除末尾的斜杠,这导致了这个问题。
总体上,预检OPTIONS请求不喜欢重定向。如果您的服务器使用301状态代码进行重定向,则可能被缓存在不同的级别。因此,请务必检查并避免此问题。

3

在这种情况下,使用Access-Control-Allow-Origin头部对请求没有帮助,因为该头部只能用于响应...

要使其起作用,您应该将此头部添加到响应中。您还可以尝试将crossorigin:true头部添加到您的请求中。


1
crossorigin:true 添加到标头对我很有帮助。干杯 - Thiago Passos

3

您可以使用自定义配置创建axios的新实例,然后使用这个新配置的实例。
创建一个名为axios-configure.js的文件,添加此可共享导出方法并使用此预配置的导入,而不是像传统方式一样直接导入axios

import axios from 'axios';
import baseUrl from './data-service';

const app = axios.create({
    baseURL: baseUrl,
    headers: {
        'Access-Control-Allow-Origin': '*',
        'Content-Type': 'application/json',
    },
    withCredentials: true
})

export default app;

请使用导出的函数,用法如下:
import axios from '../YOUR_DIRECTORY/axios-configure';
axios.get();// wont throw cors

不要导入axios from axios;
然后使用axios.get(),它将不会抛出cors错误,这对我们有用。

注意:此解决方案仅适用于在本地环境中遇到CORS问题的人,因为本地从5000开始,后端从8080开始,但在生产中,构建是从java 8080部署的,生产中没有CORS(仅在本地环境中遇到CORS问题)


3

经过长时间的尝试,终于理解了CORS的工作原理。我尝试了很多方法来修复前端和后端代码中的问题。有些方法会出现CORS错误,有些方法会导致服务器无法接收来自客户端的主体内容,还有其他错误...

最终找到了这样的解决方法。希望能对某些人有所帮助:

后端代码(NodeJS + Express)

var express = require("express");
const cors = require("cors");

var app = express();
app.use(
  cors({
    origin: "*",
  })
);

app.use(function (req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  next();
});

// your routers and codes 

我的前端代码(JS):

fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Connection: 'Keep-Alive',
      Authorization: `Bearer test`,
    },
    body: JSON.stringify(data),
  });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接