请求的资源上没有“Access-Control-Allow-Origin”标头-尝试从REST API获取数据时

我正在尝试从HP Alm的REST API获取一些数据。它与一个小curl脚本一起工作得很好-我得到了我的数据。

现在使用JavaScript、get和ES6(或多或少)执行此操作似乎是一个更大的问题。我不断收到此错误消息:

无法加载获取API。对飞行前请求的响应不会权限改造检查:无Access-Control-Allow-Origin标头存在于请求的资源上。源“http://127.0.0.1:3000”是因此不允许访问。响应的HTTP状态代码为501。如果不透明响应满足您的需求,请将请求的模式设置为'no-cors'在禁用CORS的情况下获取资源。

我明白这是因为我试图从我的localhost中获取数据,解决方案应该使用跨域资源共享(CORS)。我以为我真的做到了,但不知何故,它要么忽略了我在标头中写的内容,要么问题是别的。

那么,是不是实现有问题?我做错了吗?不幸的是,我无法检查服务器日志。我真的有点卡住了。

function performSignIn() {
let headers = new Headers();
headers.append('Content-Type', 'application/json');headers.append('Accept', 'application/json');
headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');headers.append('Access-Control-Allow-Credentials', 'true');
headers.append('GET', 'POST', 'OPTIONS');
headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));
fetch(sign_in, {//mode: 'no-cors',credentials: 'include',method: 'POST',headers: headers}).then(response => response.json()).then(json => console.log(json)).catch(error => console.log('Authorization failed : ' + error.message));}

我正在使用Chrome。我也尝试使用该ChromeCORS插件,但随后我收到另一条错误消息:

响应中“Access-Control-Allow-Origin”标头的值当请求的凭据模式为时,不得是通配符“*”“包含”。因此不允许原产地为“http://127.0.0.1:3000”访问。由XMLHttpRequest由With Cre的属性控制。

2937383 次浏览

这个答案涵盖了很多领域,所以它分为三个部分:

  • 如何使用CORS代理来避免"无Access-Control-Allow-Origin标头"问题
  • 如何避免CORS预飞行
  • 如何解决"Access-Control-Allow-Origin标头不能是通配符"问题

如何使用CORS代理来避免“无Access-Control-Allow-Origin标头”问题

如果您不控制前端代码正在向其发送请求的服务器,并且该服务器响应的问题只是缺乏必要的Access-Control-Allow-Origin标头,您仍然可以通过CORS代理发出请求来使事情正常工作。

您可以使用https://github.com/Rob--W/cors-anywhere/中的代码轻松运行自己的代理。
您还可以在2-3分钟内轻松地将自己的代理部署到Heroku,只需5个命令:

git clone https://github.com/Rob--W/cors-anywhere.gitcd cors-anywhere/npm installheroku creategit push heroku master

运行这些命令后,您最终将拥有自己的CORS Anywhere服务器,例如https://cryptic-headland-94862.herokuapp.com/

现在,用代理的URL作为请求URL的前缀:

https://cryptic-headland-94862.herokuapp.com/https://example.com

添加代理URL作为前缀会导致请求通过您的代理发出,该代理:

  1. 将请求转发到https://example.com
  2. 接收来自https://example.com的响应。
  3. Access-Control-Allow-Origin标头添加到响应中。
  4. 将该响应以及添加的标头传递回请求前端代码。

然后浏览器允许前端代码访问响应,因为带有Access-Control-Allow-Origin响应标头的响应是浏览器看到的。

即使请求触发浏览器执行CORS预检OPTIONS请求,这也有效,因为在这种情况下,代理还会发送使预检成功所需的Access-Control-Allow-HeadersAccess-Control-Allow-Methods标头。


如何避免CORS预飞行

问题中的代码触发CORS预检-因为它发送Authorization标头。

https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

即使没有,Content-Type: application/json标头也会触发预飞行。

“预检”的意思是:在浏览器尝试问题中代码中的POST之前,它首先向服务器发送OPTIONS请求,以确定服务器是否选择接收具有AuthorizationContent-Type: application/json标头的跨源POST

它与一个小卷曲脚本工作得很好-我得到我的数据。

要正确测试curl,您必须模拟浏览器发送的预飞行OPTIONS

curl -i -X OPTIONS -H "Origin: http://127.0.0.1:3000" \-H 'Access-Control-Request-Method: POST' \-H 'Access-Control-Request-Headers: Content-Type, Authorization' \"https://the.sign_in.url"

https://the.sign_in.url替换为您实际的sign_in URL。

浏览器需要的来自OPTIONS请求的响应必须具有如下标头:

Access-Control-Allow-Origin:  http://127.0.0.1:3000Access-Control-Allow-Methods: POSTAccess-Control-Allow-Headers: Content-Type, Authorization

如果OPTIONS响应不包含这些标头,浏览器将立即停止,并且永远不会尝试发送POST请求。此外,响应的HTTP状态代码必须是2xx-通常是200或204。如果是任何其他状态代码,浏览器将立即停止。

问题中的服务器以501状态代码响应OPTIONS请求,这显然意味着它试图表明它不支持OPTIONS请求。在这种情况下,其他服务器通常以405“不允许方法”状态代码响应。

因此,如果服务器用405或501或200或204以外的任何内容响应OPTIONS请求,或者如果不响应这些必要的响应标头,您将永远无法从前端JavaScript代码直接向该服务器发出POST请求。

避免为问题中的案例触发预审的方法是:

  • 如果服务器不需要Authorization请求标头,而是依赖于POST请求主体中嵌入的鉴别数据或作为查询参数
  • 如果服务器不要求POST body具有Content-Type: application/json媒体类型,而是接受POST body作为application/x-www-form-urlencoded,并带有名为json(或其他)的参数,其值是JSON数据

如何修复“Access-Control-Allow-Origin标头必须不是通配符”问题

我收到另一个错误消息:

响应中“Access-Control-Allow-Origin”标头的值当请求的凭据模式为时,不得是通配符“*”'包括'。因此不允许源'http://127.0.0.1:3000'访问。由XMLHttpRequest由With Cre的属性控制。

对于具有凭据的请求,如果Access-Control-Allow-Origin标头的值为*,浏览器不会让您的前端JavaScript代码访问响应。相反,这种情况下的值必须与您的前端代码的起源http://127.0.0.1:3000完全匹配。

请参阅MDN HTTP权限改造(CORS)文章中的凭据请求和通配符

如果您控制要向其发送请求的服务器,处理这种情况的常见方法是将服务器配置为获取Origin请求标头的值,并将其回显/反映回Access-Control-Allow-Origin响应标头的值;例如,使用nginx:

add_header Access-Control-Allow-Origin $http_origin

但这只是一个例子;其他(Web)服务器系统也有类似的方法来回显原值。


我正在使用Chrome。我还尝试使用ChromeCORS插件

ChromeCORS插件显然只是简单地将Access-Control-Allow-Origin: *标头注入浏览器看到的响应中。如果插件更聪明,它会做的是将假Access-Control-Allow-Origin响应标头的值设置为前端JavaScript代码的实际来源,http://127.0.0.1:3000

所以避免使用该插件,即使是在测试中。这只是分散注意力。要测试您从服务器获得的响应而没有浏览器过滤它们,您最好使用上面的curl -H


就问题中fetch(…)请求的前端JavaScript代码而言:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');headers.append('Access-Control-Allow-Credentials', 'true');

删除这些行。Access-Control-Allow-*标头是反应标头。您永远不想在请求中发送它们。唯一的效果是触发浏览器进行预检。

当客户端URL和服务器URL不匹配时(包括端口号)会发生此错误。在这种情况下,您需要启用CORS服务,这是跨源资源共享。

如果您正在托管Spring REST服务,那么您可以在博客文章Spring Framework中的CORS支持中找到它。

如果您使用Node.js服务器托管服务

  1. 停止Node.js服务器。
  2. npm install cors --save
  3. 将以下行添加到您的server.js
const cors=require("cors");const corsOptions ={origin:'*',credentials:true,            //access-control-allow-credentials:trueoptionSuccessStatus:200,}
app.use(cors(corsOptions)) // Use this after the variable declaration

删除此:

credentials: 'include',

使用dataType: 'jsonp'对我有用。

   async function get_ajax_data(){var _reprojected_lat_lng = await $.ajax({type: 'GET',dataType: 'jsonp',data: {},url: _reprojection_url,error: function (jqXHR, textStatus, errorThrown) {console.log(jqXHR)},success: function (data) {console.log(data);
// note: data is already json type, you//       just specify dataType: jsonpreturn data;}});

} // function

出现问题是因为您在前端中添加了以下代码作为请求标头:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');headers.append('Access-Control-Allow-Credentials', 'true');

这些头属于<强>反应,而不是请求。所以删除它们,包括行:

headers.append('GET', 'POST', 'OPTIONS');

您的请求具有'Content-Type: application/json',因此触发了所谓的CORS预检。这导致浏览器使用OPTIONS方法发送请求。有关详细信息,请参阅CORS飞行前

因此,在您的后端中,您必须通过返回响应标头来处理此预运行请求,其中包括:

Access-Control-Allow-Origin : http://localhost:3000Access-Control-Allow-Credentials : trueAccess-Control-Allow-Methods : GET, POST, OPTIONSAccess-Control-Allow-Headers : Origin, Content-Type, Accept

当然,实际语法取决于您用于后端的编程语言。

在你的前端,它应该是这样的:

function performSignIn() {let headers = new Headers();
headers.append('Content-Type', 'application/json');headers.append('Accept', 'application/json');headers.append('Authorization', 'Basic ' + base64.encode(username + ":" +  password));headers.append('Origin','http://localhost:3000');
fetch(sign_in, {mode: 'cors',credentials: 'include',method: 'POST',headers: headers}).then(response => response.json()).then(json => console.log(json)).catch(error => console.log('Authorization failed: ' + error.message));}

只是我的两分钱…关于如何使用CORS代理来解决“否#0头”问题

对于那些在后端使用php的人来说,部署“CORS代理”非常简单:

  1. 创建一个名为“no-cors.php”的文件,内容如下:

    $URL = $_GET['url'];echo json_encode(file_get_contents($URL));die();
  2. 在您的前端,执行以下操作:

    fetch('https://example.com/no-cors.php' + '?url=' + url).then(response=>{*/Handle Response/*})`

在我的情况下,Web服务器阻止了“OPTIONS”方法

检查您的Web服务器以获取选项方法

我用的是"webtier"

/www/webtier/domains/[域名]/配置/fmwconfig/组件/OHS/VCWeb1/httpd.conf

<IfModule mod_rewrite.c>RewriteEngine onRewriteCond %{REQUEST_METHOD} ^OPTIONSRewriteRule .* . [F]</IfModule>

改为

<IfModule mod_rewrite.c>RewriteEngine offRewriteCond %{REQUEST_METHOD} ^OPTIONSRewriteRule .* . [F]</IfModule>

在我的情况下,我使用下面的解决方案。

前端或Angular

post(this.serverUrl, dataObjToPost,{headers: new HttpHeaders({'Content-Type':  'application/json',})})

后端(我使用PHP)

header("Access-Control-Allow-Origin: http://localhost:4200");header('Access-Control-Allow-Methods: GET, POST, OPTIONS');header("Access-Control-Allow-Headers: Content-Type, Authorization");
$postdata = file_get_contents("php://input");$request = json_decode($postdata);print_r($request);

如果你在macOS的开发环境中使用Node.jsExpress.js作为后端,反应Axios作为前端,你需要在HTTPS下运行两边。下面是最终对我有效的方法(经过几个小时的深入研究和测试):

步骤1:创建SSL证书

只需按照如何在5分钟内让HTTPS在本地开发环境中工作的步骤操作。

您最终将获得几个文件作为运行HTTPS服务器和React Web的凭据:

server.key & server.crt

您需要将它们复制到前端和后端的根文件夹中(在生产环境中,您可能会考虑将它们复制到后端的文件夹./ssh中)。

步骤2:后端设置

我读了很多答案,建议使用“cors”包甚至设置(“Access-Control-Allow-Origin', '*'), 这就像说:“欢迎黑客访问我的网站”。就像这样做:

import express from 'express';const emailRouter = require('./routes/email');  // in my case, I was sending an email through a form in Reactconst fs = require('fs');const https = require('https');
const app = express();const port = 8000;
// CORS (Cross-Origin Resource Sharing) headers to support Cross-site HTTP requestsapp.all('*', (req, res, next) => {res.header("Access-Control-Allow-Origin", "https://localhost:3000");next();});
// Routes definitionapp.use('/email', emailRouter);
// HTTPS serverconst credentials = {key: fs.readFileSync('server.key'),cert: fs.readFileSync('server.crt')};
const httpsServer = https.createServer(credentials, app);httpsServer.listen(port, () => {console.log(`Back-end running on port ${port}`);});

如果你想测试https是否正常,你可以用下面的常量替换http服务器常量:

https.createServer(credentials, (req: any, res: any) => {res.writeHead(200);res.end("hello world from SSL\n");}).listen(port, () => {console.log(`HTTPS server listening on port ${port}...`);});

然后从Web浏览器访问它:https://localhost:8000/

第三步:前端设置

这是来自React前端的Axios请求:

    await axios.get(`https://localhost:8000/email/send`, {params: { /* Whatever data you want to send */ },headers: {'Content-Type': 'application/json',}})

现在,您需要使用我们已经创建的SSL凭据在HTTPS模式下启动您的React Web。在您的macOS终端中键入此内容:

HTTPS=true SSL_CRT_FILE=server.crt SSL_KEY_FILE=server.key npm start

此时,您正在从前端发送来自端口3000的HTTPS连接的请求,由后端端口8000的HTTPS连接接收。CORS应该对此感到满意;)

使用Node.js,如果您使用的是路由器,请确保在路由器之前添加CORS。否则,您仍然会收到CORS错误。如下所示:

const cors = require('cors');
const userRouter = require('./routers/user');
expressApp = express();expressApp.use(cors());expressApp.use(express.json());expressApp.use(userRouter);

添加mode:no-cors可以避免API中的CORS问题。

fetch(sign_in, {mode: 'no-cors',credentials: 'include',method: 'POST',headers: headers}).then(response => response.json()).then(json => console.log(json)).catch(error => console.log('Authorization failed : ' + error.message));}

尝试在下面的代码中添加所有这些标头在每条路线之前,您都要在应用程序中定义,而不是在路线之后

app.use((req, res, next) =>{res.setHeader('Access-Control-Allow-Origin', '*');res.setHeader('Access-Control-Allow-Headers','Origin, X-Requested-With, Content-Type,Accept, Authortization');res.setHeader('Acces-Control-Allow-Methods','GET, POST, PATCH, DELETE');

如果您在部署React应用程序以netlify时遇到此错误,请使用以下步骤。

第1步:在React应用的根文件夹中创建netlify.toml文件。

第2步:复制粘贴此代码:

`[[redirects]]from = "/cors-proxy/*"to = ":splat"status = 200force = true`

输入图片描述

step3:以这种方式更新您的get/axios api:

输入图片描述

我花了一段时间才弄清楚这一点。

使用下面的npm模块。这实际上挽救了生命。

https://www.npmjs.com/package/local-cors-proxy

您将收到CORS错误,例如以下URL

https://www.google.co.in/search/list

成功安装后(本地代理服务)全局npm install -g local-cors-proxy并设置代理URL即CORS URL。例如,下面的CORS问题localhost。因此,您需要为CORS问题域添加域名(https://www.google.co.in)和端口(--port 8010)。更多请查看链接https://www.npmjs.com/package/local-cors-proxy

lcp --proxyUrl https://www.google.co.in --port 8010

设置成功后,它将生成如下所示的本地代理URL。

http://localhost:8010/proxy

在您的项目API URL中使用该域名。

API完整URL:

http://localhost:8010/proxy/search/list

在本地项目中获取没有CORS问题的响应。

如果您的API是用ASP.NET Core编写的,请按照以下步骤操作:

  • 安装Microsoft. Asp Net Core. Cor s包。

  • 在文件Startup.cs的配置服务方法中添加以下行:

    services.AddCors();
  • 在文件startup.cs配置方法中添加以下行:

    app.UseCors(options =>options.WithOrigins("http://localhost:8080").AllowAnyHeader().AllowAnyMethod());
  • 确保在-app.UseRouting();之后添加此内容

    请参阅下图(来自MSDN)以查看中间件顺序:

    https://i.stack.imgur.com/vQ4yT.png

CORS问题的可能原因

  • 检查您的服务器端访问标头:请参阅此链接

  • 检查浏览器中从服务器接收到的请求标头。下图显示了标头

    在此输入图片描述

  • 如果您使用fetch方法并尝试访问跨源请求,请确保mode:cors在那里。参考此链接

  • 有时,如果程序中存在问题,您也会遇到CORS问题,因此请确保您的代码正常工作。

  • 确保在API中处理OPTION方法。

在过去的几年里,我遇到过几次这个错误——似乎突然出现在一个以前运行的网站上。

我确定Chrome(以及可能的其他浏览器)可以在服务器上发生一些不相关的错误时返回此错误,该错误阻止它处理CORS请求(并且在返回HTTP 500错误之前)。

这些都发生在. NET Core环境中,我不确定它是否会发生在其他环境中。

无论如何,如果您的代码以前运行过并且看起来是正确的,请考虑调试以查找是否有其他错误正在触发,然后再疯狂地尝试解决实际上不存在的错误。

对于一个Node.js和后端我使用这个:)

app.use(function(req, res, next) {res.header("Access-Control-Allow-Origin", "YOUR-DOMAIN.TLD"); // Update to match the domain you will make the request fromres.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");next();});

更多详情:ExpressJS上的CORS

当客户端用来调用我们的后端服务从他的主机username.companyname.com,他用来得到上述错误

需要做两件事:

  1. 在返回响应时,发送键为访问-控制-允许-起源且值为*的标头:

     context.Writer.Header()["Access-Control-Allow-Origin"] = []string{"*"} // Important to avoid a CORS error
  2. 使用 CORS库将授权证书设置为false,将所有来源设置为true。

    在此输入图片描述

我犯了很多次这个错误,正因为如此,我给你们所有人做了一个“清单”。

  • 在您的项目上启用CORS:如果您使用Node.js(通过示例),您可以使用:

    npm install cors;import cors from 'cors';app.use(cors());
  • 您可以像这样手动设置标题(如果需要):

    app.use((req, res, next) => {res.setHeader('Access-Control-Allow-Origin', '*');res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authortization');res.setHeader('Acces-Control-Allow-Methods', 'GET, POST, PATCH, DELETE');
  • 请记住在前端项目的API链接中添加超文本传输协议://,如果请求URL不是HTTP或HTTPS,Chrome不要等一些浏览器会接受使用CORS的请求:

    http://localhost:3000/api
  • 检查您的项目是否使用proxy.config.js文件。请参阅使用Angular CLI代理修复CORS错误

在2021年12月,Chrome97,不允许Authorization: Bearer ...,除非它在Access-Control-Allow-Headers飞行前响应中(忽略*)。它产生了以下警告:

[Deprecation] authorization will not be covered by the wildcard symbol (*)

见:Chrome企业发行说明,Chrome97

它似乎也在Access-Control-Allow-Origin上对*强制执行相同的限制。如果你想恢复类似*的行为,现在它被阻止了,你可能必须读取请求者的源并将其作为飞行前响应中允许的源返回。

在某些情况下,当存在其他无效凭据(例如:过期的智威汤逊)时,库可能会删除Access-Control-Allow-Origin响应标头。然后,浏览器会显示“不存在'Access-Control-Allow-Origin'标头”错误而不是实际错误(在本示例中可能是过期的JWT)。确保您的库不会删除标头并混淆客户端。

在我的情况下,我必须在所有现有中间件下方添加一个自定义标头中间件。我认为一些中间件可能与Access-Control-Allow-Origin标头冲突,并尝试根据他们的需要设置它。

所以代码应该是这样的:

app.use(cors());
....all other middleware here
app.use(function (req, res, next) {res.header("Access-Control-Allow-Origin", "http://localhost:3000");res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");next();});...your routes

对于那些使用ASP.NET核心:

在我的例子中,我使用JavaScript从存储在API(服务器)上的图像创建blob,因此URL指向该资源。在该API的program.cs类中,我已经有了CORS策略,但它不起作用。

在我阅读了关于这个问题的微软留档(阅读第一段)之后,据说如果你想通过使用JavaScript(这就是我试图做的)访问服务器上的资源,那么你必须将app.UseCors();<强>之前称为app.UseStaticFiles();,这通常是相反的。

我的program.cs文件:

const string corsPolicyName = "ApiCORS";
builder.Services.AddCors(options =>{options.AddPolicy(corsPolicyName, policy =>{policy.WithOrigins("https://localhost:7212");});});
...
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI(settings =>{settings.DisplayRequestDuration();settings.EnableTryItOutByDefault();});
app.UseHttpsRedirection();
app.UseCors(corsPolicyName); // 👈 This should be above the UseStaticFiles();
app.UseStaticFiles(); // 👈 Below the UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseApiCustomExceptionHandler();
app.MapControllers();
app.Run();

在我的情况下,解决方案是愚蠢的…你允许的起源不应该在最后有一个斜线。

例如,https://example.com/->https://example.com

使用WebAPI构建。Net Core 6.0

以上这些对我都不管用…这就成了

// global cors policyapp.UseCors(x => x.AllowAnyMethod().AllowAnyHeader().SetIsOriginAllowed(origin => true) // allow any origin.AllowCredentials());

信用:https://stackoverflow.com/a/70660054/8767516

在我的反应/快递应用程序中遇到这个问题。在server.js(或您的服务器文件名)中添加以下代码为我解决了这个问题。安装cors,然后

const cors = require('cors');app.use(cors({origin: 'http://example.com', // use your actual domain name (or localhost), using * is not recommendedmethods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'],allowedHeaders: ['Content-Type', 'Origin', 'X-Requested-With', 'Accept', 'x-client-key', 'x-client-token', 'x-client-secret', 'Authorization'],credentials: true}))