No' Access-Control-Allow-Origin'当尝试从REST API获取数据时,头部存在于请求的资源上

时间:2017-05-09 13:47:48

标签: javascript api cors fetch-api preflight

我试图从HP Alm的REST API中获取一些数据。它适用于小卷曲脚本 - 我得到了我的数据。

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

  

无法加载Fetch API。对预检请求的响应不是   通过访问控制检查:否'访问控制 - 允许 - 来源'标题是   出现在请求的资源上。起源' http://127.0.0.1:3000'是   因此不允许访问。响应具有HTTP状态代码501。   如果不透明响应满足您的需求,请将请求的模式设置为   '无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。我也尝试使用Chrome CORS插件,但后来又收到了另一条错误消息:

  

' Access-Control-Allow-Origin'响应中的标头   不能是通配符' *'当请求的凭据模式是   '包括&#39 ;.起源' http://127.0.0.1:3000'因此是不允许的   访问。由...发起的请求的凭据模式   XMLHttpRequest由withCredentials属性控制。

19 个答案:

答案 0 :(得分:407)

这个答案涵盖了很多方面,因此它分为三个部分:

  • 如何使用CORS代理绕过“No Access-Control-Allow-Origin header”问题
  • 如何避免CORS预检
  • 如何修复“Access-Control-Allow-Origin标头不能是通配符”问题

如何使用CORS代理绕过“No Access-Control-Allow-Origin标头”问题

如果您不控制服务器,您的前端JavaScript代码正在向其发送请求,而该服务器的响应问题只是缺少必要的Access-Control-Allow-Origin标头,您仍然可以获取通过CORS代理提出请求。为了说明它是如何工作的,首先是一些不使用CORS代理的代码:



const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(url)
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))




catch块被点击的原因是,浏览器阻止该代码访问从https://example.com返回的响应。浏览器这样做的原因是,响应缺少Access-Control-Allow-Origin响应标头。

现在,这是完全相同的示例,但只是添加了CORS代理:



const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(proxyurl + url) // https://cors-anywhere.herokuapp.com/https://example.com
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))




注意:如果https://cors-anywhere.herokuapp.com在您尝试时已关闭或不可用,请参阅下文,了解如何在Heroku中部署您自己的CORS Anywhere服务器3分钟。

上面的第二个代码段可以成功访问响应,因为获取请求URL并将其更改为https://cors-anywhere.herokuapp.com/https://example.com - 只需在其前面加上代理URL - 导致请求通过该代理进行,然后:

  1. 将请求转发至https://example.com
  2. 收到https://example.com的回复。
  3. Access-Control-Allow-Origin标题添加到回复中。
  4. 将带有添加标题的响应传递回请求的前端代码。
  5. 然后,浏览器允许前端代码访问响应,因为带有Access-Control-Allow-Origin响应头的响应是浏览器看到的。

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

    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/。因此,不要在您的请求网址前加https://cors-anywhere.herokuapp.com前缀,而是在其前面加上您自己实例的网址;例如,https://cryptic-headland-94862.herokuapp.com/https://example.com

    因此,如果你去尝试使用https://cors-anywhere.herokuapp.com,你会发现它已经失效(它有时会是这样),然后考虑获得一个Heroku帐户(如果你还没有)并花2或3分钟完成上述步骤,在Heroku上部署自己的CORS Anywhere服务器。

    无论您是自己运行还是使用https://cors-anywhere.herokuapp.com或其他开放代理,即使请求是触发浏览器执行CORS预检OPTIONS请求的解决方案,此解决方案仍然有效 - 因为例如,代理还会发回预检成功所需的Access-Control-Allow-HeadersAccess-Control-Allow-Methods标头。

    如何避免CORS预检

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

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

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

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

      

    使用小卷曲脚本可以很好地工作 - 我得到了我的数据。

    要使用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网址。

    浏览器需要从OPTIONS请求中看到的响应必须包含以下标题:

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

    如果OPTIONS响应不包含这些标头,那么浏览器就会停在那里,甚至不会尝试发送POST请求。此外,响应的HTTP状态代码必须是2xx(通常为200或204)。如果是任何其他状态代码,浏览器将停在那里。

    问题中的服务器正在使用501状态代码响应OPTIONS请求,这显然意味着它正在尝试表明它没有实现对OPTIONS请求的支持。在这种情况下,其他服务器通常会响应405“Method not allowed”状态代码。

    如果服务器使用405或501或其他任何内容响应POST请求,那么您永远无法从前端JavaScript代码直接向该服务器发出OPTIONS个请求200或204或者如果没有回复那些必要的响应标题。

    避免在问题中触发案件预检的方法是:

    • 如果服务器不需要Authorization请求标头,而是(例如)依赖嵌入在POST请求正文中的身份验证数据或作为查询参数
    • 如果服务器不要求POST正文拥有Content-Type: application/json媒体类型,而是将POST正文作为application/x-www-form-urlencoded接受,其参数名为{{1其值为JSON数据的(或其他)

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

      

    我收到另一条错误消息:

         
        

    ' Access-Control-Allow-Origin'响应中的标头     不能是通配符' *'当请求的凭据模式是     '包括&#39 ;.起源' http://127.0.0.1:3000'因此是不允许的     访问。由...发起的请求的凭据模式     XMLHttpRequest由withCredentials属性控制。

      

    对于包含凭据的请求,如果json响应标头的值为Access-Control-Allow-Origin,浏览器将不允许您的前端JavaScript代码访问响应。相反,该情况下的值必须与您的前端代码的原点*完全匹配。

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

    如果你控制要发送请求的服务器,那么处理这种情况的一种常用方法是配置服务器获取http://127.0.0.1:3000请求头的值,并回显/反映回来进入Origin响应标头的值。例如,使用nginx:

    Access-Control-Allow-Origin

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

      

    我正在使用Chrome。我也尝试过使用Chrome CORS插件

    Chrome CORS插件显然只是简单地在浏览器看到的响应中注入add_header Access-Control-Allow-Origin $http_origin 标头。如果插件更智能,它将要做的是将假的Access-Control-Allow-Origin: *响应标头的值设置为前端JavaScript代码Access-Control-Allow-Origin的实际来源。

    因此,即使是测试,也要避免使用该插件。这只是一种分心。如果您想测试从服务器获得的响应而没有浏览器过滤它们,那么最好使用上面的http://127.0.0.1:3000

    至于问题中curl -H请求的前端JavaScript代码:

    fetch(…)

    删除这些行。 headers.append('Access-Control-Allow-Origin', 'http://localhost:3000'); headers.append('Access-Control-Allow-Credentials', 'true'); 标头是响应标头。您永远不想在请求中发送它们。唯一的影响就是触发浏览器进行预检。

答案 1 :(得分:49)

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

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

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

  1. 停止Node.js服务器。
  2. npm install cors --save
  3. 将以下行添加到您的server.js

    var cors = require('cors')
    
    app.use(cors()) // Use this after the variable declaration
    

答案 2 :(得分:10)

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

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

那些标头属于 响应 ,而不是请求。因此,删除它们,包括以下行:

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

您的请求具有“ Content-Type:应用程序/ json”,因此触发了所谓的CORS预检。这导致浏览器使用OPTIONS方法发送了请求。有关详细信息,请参见CORS preflight
因此,在您的后端中,您必须通过返回包括以下内容的响应标头来处理此预检请求:

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

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

在您的前端,应该像这样:

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

答案 3 :(得分:4)

就我而言,我使用以下解决方案

前端或角度

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);

答案 4 :(得分:1)

我知道我迟到了,但这会对某人有所帮助

CORS 问题的可能原因

  • 检查您的服务器端访问标头:Refer to this link
  • 在浏览器中检查从服务器收到的请求头,下图显示了头 enter image description here
  • 如果您使用 fetch 方法并尝试访问跨域请求,请确保存在 mode:cors。请参阅此link
  • 有时,如果程序中存在问题,您也会遇到 CORS 问题,因此请确保您的代码正常工作。
  • 确保处理 API 中的 OPTION 方法。

答案 5 :(得分:0)

我正在使用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");
                }
            };
        }

答案 6 :(得分:0)

只需两美分...关于如何使用CORS代理解决“无访问权限-允许-来源标头”问题

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

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

    $ URL = $ _GET ['url']; 回声json_encode(file_get_contents($ URL)); die();

  2. 在您的前端,执行以下操作:

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

答案 7 :(得分:0)

就我而言,Web服务器禁止使用“ OPTIONS”方法

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

  

我正在使用“网络层”

     

/ www / webtier / domains / [域名] /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>

答案 8 :(得分:0)

使用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               

答案 9 :(得分:0)

我在本地遇到此错误。 我以管理员身份运行Visual Studio,并解决了该问题。

答案 10 :(得分:0)

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

ARG

答案 11 :(得分:0)

添加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));
}

答案 12 :(得分:0)

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

<块引用>

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

您收到 CORS 错误,例如如下网址

<块引用>

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

成功安装后 (local-cors-proxy) 全局 npm install -g local-cors-proxy 并设置 CORS url 的代理 URL。例如下面的 CORS 问题进入本地主机。所以需要添加CORS问题域的域名和端口。

<块引用>

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

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

<块引用>

http://localhost:8010/proxy

在您的项目 API 网址中使用该域名。 API 完整网址

<块引用>

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

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

答案 13 :(得分:0)

如果你的API是用asp dot net core编写的。然后请按照以下步骤操作:

  • 安装 Microsoft.AspNetCore.Cors 包。

  • 在 Startup.cs 的 ConfigureServices 方法中添加以下行:

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

    app.UseCors(options =>
         options.WithOrigins("http://localhost:8080")
                .AllowAnyHeader()
                .AllowAnyMethod());
    
    make sure you add this before - app.UseRouting();
    

答案 14 :(得分:0)

发生这种情况是因为您无法从同一网站获取数据。因此,如果您在 abc.com 上,则无法从 api.abc.com 获取。保存网站政策。

解决方法如下:https://www.youtube.com/watch?v=RyH3qu3smVI&ab_channel=CoderDmitri

在你的 server.js -> 终端中安装这个包:

npm install cors

在您的代码之上:

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

这将允许来自同一域的用户立即获取数据。

答案 15 :(得分:0)

删除此:

credentials: 'include',

答案 16 :(得分:-1)

如果在MacOS的开发环境中将NodeJS / Express用作后端,将ReactJS / axios用作前端,则需要在https下运行双方。以下是它最终对我有用的东西(经过数小时的深入潜水和测试):

步骤1:创建SSL证书

只需遵循How to get HTTPS working on your local development environment in 5 minutes

中的步骤

您最终将获得几个文件,以用作运行https服务器和ReactJS网站的凭据:

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

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

第3步:前端设置

这是来自ReactJS前端的axios请求:

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

现在,您需要使用我们已经创建的SSL凭证以https方式启动ReactJS网站。在MacOS终端中输入以下内容:

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

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

答案 17 :(得分:-2)

最快的解决方法是安装moesif CORS扩展程序。

https://chrome.google.com/webstore/detail/moesif-orign-cors-changer/digfbfaphojjndkpccljibejjbppifbc?hl=en-US

安装后,在浏览器中单击它以激活扩展。确保图标的标签从“关闭”到enter image description here 到“上” enter image description here; 然后刷新您的应用程序,您的API请求现在应该可以工作了! 该插件肯定解决了这个问题。但是,此修复程序仅适用于您自己的计算机。 在本地开发中,最好安装可以帮助您克服错误的插件。

答案 18 :(得分:-2)

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

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

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

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

enter image description here

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

enter image description here

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