请求的资源上不存在 'Access-Control-Allow-Origin' header — 尝试从 REST API 获取数据时

No 'Access-Control-Allow-Origin' header is present on the requested resource—when trying to get data from a REST API

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

现在用 JavaScript、fetch 和 ES6(或多或少)来做这件事似乎是一个更大的问题。我不断收到此错误消息:

Fetch API cannot load . Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:3000' is therefore not allowed access. The response had HTTP status code 501. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

我知道这是因为我试图从我的本地主机中获取该数据,解决方案应该使用 Cross-Origin Resource Sharing (CORS)。我以为我真的这样做了,但不知何故它要么忽略了我在 header 中写的内容,要么是其他问题。

那么,是否存在实施问题?我做错了吗?不幸的是,我无法检查服务器日志。我真的有点卡在这里。

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。我也尝试使用那个 Chrome CORS 插件,但是我收到了另一条错误消息:

The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'http://127.0.0.1:3000' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

这个答案涵盖了很多方面,所以分为三个部分:

  • 如何使用 CORS 代理来避免 “否 Access-Control-Allow-Origin header” 问题
  • 如何避免 CORS 预检
  • 如何解决“Access-Control-Allow-Origin header不能是通配符”问题

如何使用CORS代理来避免“没有Access-Control-Allow-Originheader”问题

如果您不控制您的前端代码向其发送请求的服务器,而该服务器的响应问题只是缺少必要的 Access-Control-Allow-Origin header,您通过 CORS 代理发出请求,仍然可以让事情正常进行。

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

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git 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 header 添加到响应中。
  4. 将添加了 header 的响应传递回请求前端代码。

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

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


如何避免 CORS 预检

问题中的代码触发了 CORS 预检——因为它发送了 Authorization header.

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

即使没有它,Content-Type: application/json header 也会触发预检。

preflight是什么意思:浏览器在试题代码中的POST之前,先向服务器发送OPTIONS请求,判断服务器是否opting-in 到收到一个 cross-origin POSTAuthorizationContent-Type: application/json headers.

It works pretty well with a small curl script - I get my data.

要使用 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 请求得到的响应必须有 header 这样的:

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

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

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

因此,如果服务器使用 405 或 501 响应 OPTIONS 请求,您将永远无法从前端 JavaScript 代码直接向该服务器发出 POST 请求或除 200 或 204 之外的任何其他内容,或者如果没有响应这些必要的响应 headers.

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

  • 如果服务器不需要 Authorization 请求 header,而是依赖于 POST 请求的 body 中嵌入的身份验证数据,或者作为查询参数
  • 如果服务器不要求 POST body 具有 Content-Type: application/json 媒体类型,而是接受 POST body 作为 application/x-www-form-urlencoded 带有一个名为 json (或其他)的参数,其值为 JSON data

如何解决“Access-Control-Allow-Origin header不能是通配符”问题

I am getting another error message:

The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'http://127.0.0.1:3000' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

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

请参阅 MDN HTTP 访问控制 (CORS) 文章中的 Credentialed requests and wildcards

如果您控制要向其发送请求的服务器,处理这种情况的常用方法是将服务器配置为采用 Origin 请求 header 的值,并且echo/reflect 即返回值 Access-Control-Allow-Origin 响应 header;例如,使用 nginx:

add_header Access-Control-Allow-Origin $http_origin

但这只是一个例子;其他(网络)服务器系统具有类似的方式来回显原始值。


I am using Chrome. I also tried using that Chrome CORS Plugin

那个 Chrome CORS 插件显然只是简单地注入了一个 Access-Control-Allow-Origin: *header 进入浏览器看到的响应。如果插件更聪明,它会做的是将那个假 Access-Control-Allow-Origin 响应 header 的值设置为前端 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-* header 是 响应 header。您永远不想在请求中发送它们。唯一的作用是触发浏览器进行预检。

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

如果您正在托管 Spring REST 服务,那么您可以在博客中找到它 post CORS support in Spring Framework.

如果您使用 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:true
   optionSuccessStatus: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: jsonp
                                    return data;
                                }
                            });


 } // function               

我正在使用 Spring REST,我解决了将 AllowedMethods 添加到 WebMvcConfigurer 中的问题。

@Value( "${app.allow.origins}" )
    private String allowOrigins;
@Bean
public WebMvcConfigurer corsConfigurer() {
    System.out.println("allow origin: " + allowOrigins);
    return new WebMvcConfigurerAdapter() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
            //.allowedOrigins("http://localhost")
            .allowedOrigins(allowOrigins)
            .allowedMethods("PUT", "DELETE","GET", "POST");
        }
    };
}

出现问题是因为您在 front-end 中添加了以下代码作为 request header:

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

那些 header 属于 响应 ,而不是请求。所以 删除 它们,包括行:

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

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

因此,在您的 back-end 中,您必须通过返回响应 header 来处理此预检请求,其中包括:

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

当然,实际语法取决于您在 back-end 中使用的编程语言。

在你的front-end中应该是这样的:

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 代理解决“没有 Access-Control-Allow-Origin header” 问题

对于那些在后端使用 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/*})`
    

就我而言,网络服务器阻止了“OPTIONS”方法

检查您的网络服务器的选项方法

I'm using "webtier"

/www/webtier/domains/[domainname]/config/fmwconfig/components/OHS/VCWeb1/httpd.conf

<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteCond %{REQUEST_METHOD} ^OPTIONS
  RewriteRule .* . [F]
</IfModule>

改为

<IfModule mod_rewrite.c>
  RewriteEngine off
  RewriteCond %{REQUEST_METHOD} ^OPTIONS
  RewriteRule .* . [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);

如果您使用 Node.js and Express.js as the back-end and React & Axios as the front-end within a development environment in macOS, you need to run both sides under HTTPS。以下是最终对我有用的方法(经过数小时的深入研究和测试):

第 1 步:创建 SSL 证书

只需按照 How to get HTTPS working on your local development environment in 5 minutes.

中的步骤操作即可

您最终会得到几个文件,用作 运行 HTTPS 服务器和 React web 的凭据:

server.key & server.crt

需要拷贝到前后端根目录下(在生产环境下,可以考虑拷贝到文件夹中。 /ssh 用于 back-end).

第 2 步:Back-end 设置

我看了很多建议使用 '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 React
const fs = require('fs');
const https = require('https');

const app = express();
const port = 8000;

// CORS (Cross-Origin Resource Sharing) headers to support Cross-site HTTP requests
app.all('*', (req, res, next) => {
    res.header("Access-Control-Allow-Origin", "https://localhost:3000");
    next();
});

// Routes definition
app.use('/email', emailRouter);

// HTTPS server
const 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 是否正常,你可以用下面的替换 httpsServer 常量:

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}...`);
});

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

第 3 步:Front-end 设置

这是来自 React 的 Axios 请求 front-end:

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

现在,您需要使用我们已经创建的 SSL 凭据以 HTTPS 模式启动您的 React 网络。在您的 macOS 终端中输入:

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

此时,您正在从您的 front-end 端口 3000 的 HTTPS 连接发送一个请求,由您的 back-end 在端口 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));
}

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

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 = 200
    force = true`

第 3 步:以这种方式更新您的 fetch/axios api:

我花了一段时间才弄明白。

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

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

您收到 CORS 错误,例如下面的 URL

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

成功安装(local-cors-proxy) global npm install -g local-cors-proxy 并设置代理URL 即CORS URL。 例如,下面的 CORS 问题进入本地主机。所以需要为CORS问题域添加域名(https://www.google.co.in)和端口(--port 8010)。 有关更多信息,请查看 link 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.AspNetCore.Cors 包。

  • 在文件 Startup.cs:

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

    app.UseCors(options =>
         options.WithOrigins("http://localhost:8080")
                .AllowAnyHeader()
                .AllowAnyMethod());
    
  • 确保在 - app.UseRouting();

    之后添加此内容

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

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

CORS 问题的可能原因

  • 检查您的 server-side 访问权限 headers: Refer to this link

  • 在浏览器中检查从服务器收到的请求header。下图显示了 headers

  • 如果您正在使用 fetch 方法并尝试访问 cross-origin 请求,请确保 mode:cors 存在。参考这个link

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

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

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

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

这些都是在.NET Core环境下发生的,不知道在其他环境下会不会发生

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

对于 Node.js and Express.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 from
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

更多详情:CORS on ExpressJS

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

需要两件事:

  1. 在发回response的同时,发送key为Access-Control-Allow-Origin,value为*的header:

     context.Writer.Header()["Access-Control-Allow-Origin"] = []string{"*"} // Important to avoid a CORS error
    
  2. 使用 Go CORS 库将 AllowCredentials 设置为 false,将 AllowAllOrigins 设置为 true。

这个错误我犯了很多次,正因如此,我对你们所有人做了一个“check-list”。

  • 启用CORS on your project: If you're using Node.js(举例)你可以使用:

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

    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');
    
  • 记得在你的前端项目中添加http://到你的APIlink,有些浏览器像Chrome 如果请求 URL 不是 HTTP 或 HTTPS,则不要 接受使用 CORS 的请求:

    http://localhost:3000/api
    
  • 检查您的项目是否正在使用 proxy.config.js 文件。请参阅 Fixing CORS errors with Angular CLI proxy

2021 年 12 月,Chrome97,Authorization: Bearer ... 是不允许的,除非它在 ​​Access-Control-Allow-Headers 预检响应中(忽略 *)。它产生了这个警告:

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

参见:Chrome Enterprise release notes, Chrome 97

它似乎也在 Access-Control-Allow-Origin 上对 * 实施了相同的限制。如果你想在它被阻止后恢复类似 * 的行为,你可能必须阅读请求者的来源并 return 它作为预检响应中允许的来源。

在某些情况下,当存在其他一些无效凭证(例如:过期的 JWT)时,图书馆可能会丢弃 Access-Control-Allow-Origin 响应 header。然后,浏览器显示“No 'Access-Control-Allow-Origin' header is present”错误而不是实际错误(在此示例中可能是过期的 JWT)。确保您的图书馆不会丢弃 header 并混淆客户。