首页 文章

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

提问于
浏览
150

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

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

Fetch API无法加载 . 对预检请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头 . 因此不允许来源“http://127.0.0.1:3000”访问 . 响应具有HTTP状态代码501.如果不透明响应满足您的需要,请将请求的模式设置为“no-cors”以获取禁用CORS的资源 .

我知道这是因为我试图从我的localhost中获取该数据,解决方案应该使用CORS . 现在我以为我确实这样做了,但不管怎样它要么忽略我在 Headers 中写的内容,要么问题是别的?

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

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插件,但后来我收到另一条错误消息:

当请求的凭据模式为“include”时,响应中“Access-Control-Allow-Origin”标头的值不能是通配符“*” . 因此不允许来源“http://127.0.0.1:3000”访问 . XMLHttpRequest发起的请求的凭据模式由withCredentials属性控制 .

4 回答

  • 0

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

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

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

    • 停止Node.js服务器 .

    • npm安装cors

    • 将以下行添加到server.js

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

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

    • 如何避免CORS预检

    • 如何使用CORS代理来解决“No Access-Control-Allow-Origin header”问题

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


    How to avoid the CORS preflight

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

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

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

    什么“预检”意味着:在浏览器尝试问题代码中的 POST 之前,它将首先向服务器发送 OPTIONS 请求,以确定服务器是否选择接收包含以下内容的跨源 POST AuthorizationContent-Type: application/json Headers .

    它适用于小卷曲脚本 - 我得到了我的数据 .

    要使用 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:3000
    Access-Control-Allow-Methods: POST
    Access-Control-Allow-Headers: Content-Type, Authorization
    

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

    问题中的服务器使用501状态代码响应 OPTIONS 请求,这显然意味着它正在尝试表明它没有实现对 OPTIONS 请求的支持 . 在这种情况下,其他服务器通常以405“方法不允许”状态代码进行响应 .

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

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

    • 如果服务器不需要 Authorization 请求标头,而是(例如)依赖于嵌入在 POST 请求正文中的身份验证数据或作为查询参数

    • 如果服务器不要求 POST 正文具有 Content-Type: application/json 媒体类型,而是接受 POST 正文作为 application/x-www-form-urlencoded ,其参数名为 json (或其他),其值为JSON数据


    How to use a CORS proxy to get around “No Access-Control-Allow-Origin header” problems

    如果您不控制服务器,您的前端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?"))
    

    Note: If https://cors-anywhere.herokuapp.com is down or unavailable when you try it, then see below for how to deploy your own CORS Anywhere server at Heroku in just 2-3 minutes.

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

    • 将请求转发给 https://example.com .

    • 收到 https://example.com 的回复 .

    • Access-Control-Allow-Origin 标头添加到响应中 .

    • 将带有添加的标头的响应传递回请求的前端代码 .

    然后,浏览器允许前端代码访问响应,因为具有 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 请求URL,而是在前缀中添加您自己实例的URL;例如,https://cryptic-headland-94862.herokuapp.com/https://example.com .

    So if when you go to try to use https://cors-anywhere.herokuapp.com, you find it’s down (有时会这样),然后考虑获得一个Heroku帐户(如果你还没有)并花2或3分钟完成上述步骤,在Heroku上部署你自己的CORS Anywhere服务器 .

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


    How to fix “Access-Control-Allow-Origin header must not be the wildcard” problems

    我收到另一条错误消息:当请求的凭据模式为“include”时,响应中的“Access-Control-Allow-Origin”标头的值不能是通配符“*” . 因此不允许来源“http://127.0.0.1:3000”访问 . XMLHttpRequest发起的请求的凭据模式由withCredentials属性控制 .

    对于包含凭据的请求,如果 Access-Control-Allow-Origin 响应标头的值为 * ,则浏览器不会让您的前端JavaScript代码访问响应 . 相反,该情况下的值必须与您的前端代码的原点完全匹配, http://127.0.0.1:3000 .

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

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

    add_header Access-Control-Allow-Origin $http_origin
    

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


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

    Chrome CORS插件显然只是简单地在浏览器看到的响应中注入 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-* 标头是响应标头 . 你永远不想在请求中发送它们 . 唯一的影响就是触发浏览器进行预检 .

  • 277

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

    删除这个:

    credentials: 'include',
    

相关问题