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

dan*_*ski 345 javascript api cors preflight fetch-api

我正在尝试从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.现在我以为我确实这样做了,但不管怎么说它要么忽略我在标题中写的内容,要么问题是别的什么?

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

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));
}
Run Code Online (Sandbox Code Playgroud)

我正在使用Chrome.我也尝试使用Chrome CORS插件,但后来我收到另一条错误消息:

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

sid*_*ker 595

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

  • 如何使用CORS代理来解决"No Access-Control-Allow-Origin header"问题
  • 如何避免CORS预检
  • 如何修复"Access-Control-Allow-Origin标头不能是通配符"问题

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

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

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?"))
Run Code Online (Sandbox Code Playgroud)

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

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

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

然后,浏览器允许前端代码访问响应,因为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
Run Code Online (Sandbox Code Playgroud)

运行这些命令后,您最终将拥有自己运行的CORS Anywhere服务器,例如https://cryptic-headland-94862.herokuapp.com/.因此,不要在您的请求URL前加上https://cors-anywhere.herokuapp.com前缀,而是使用您自己实例的URL作为前缀; 例如,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服务器发送请求 - 以确定服务器是否选择接收POST包含AuthorizationContent-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"
Run Code Online (Sandbox Code Playgroud)

... 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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

我收到另一条错误消息:

当请求的凭据模式为"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请求和通配符.

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

add_header Access-Control-Allow-Origin $http_origin
Run Code Online (Sandbox Code Playgroud)

但这只是一个例子; 其他(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');
Run Code Online (Sandbox Code Playgroud)

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

  • 很棒的答案,我的问题是远程服务器没有响应 OPTIONS 请求,所以在摆弄了看起来像年龄的请求和标头之后,我通过删除标头“Content-Type”和“Access-Control-Allow-Origin”解决了这个问题- 谢谢你! (9认同)
  • `https://cors-anywhere.herokuapp.com/` 现在不再可用。客户端现在将收到“403 Forbidden”错误 - 除非开发人员明确请求“暂时”通过。以下是公告:https://github.com/Rob--W/cors-anywhere/issues/301。我建议简单地从答案中删除“cors-anywhere”引用,因为它不再有用。 (9认同)
  • 实际上,将新版本的扩展上传到 Azure DevOps 后,您必须在添加新范围“范围”时在 https://dev.azure.com/{your_organization}/_settings/extensions?tab=installed 中更新它的权限": 清单文件中的 ["vso.build"]。 (2认同)

Rak*_*esh 73

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

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

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

  1. 停止Node.js服务器.
  2. npm安装cors
  3. 将以下行添加到server.js

    var cors = require('cors')
    
    app.use(cors()) // Use this after the variable declaration
    
    Run Code Online (Sandbox Code Playgroud)

  • 工作没有任何问题...非常有用春季资源链接..非常感谢 (4认同)
  • `包括端口号`:( (4认同)
  • 这个问题其实就是提出一个请求。那么假设您无权访问 REST 服务器?难道就没有办法提出请求吗? (3认同)
  • 注意:您还需要从客户端的“fetch()”选项中删除“mode: 'no-cores'”设置。除非您的请求中包含“mode: 'cores'”,否则您的 JSON 正文不会被发送。 (2认同)

Lex*_*oft 24

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

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

这些标头属于response,而不是request。因此删除它们,包括以下行:

headers.append('GET', 'POST', 'OPTIONS');
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

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

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));
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是最佳答案 - 我真的不喜欢绕过 CORS 的想法,尤其是通过第三方路由它。 (15认同)
  • @mitsu如果您参考这一行:let headers = new Headers(); 上面,那么它是 fetch API 的接口,用于对 http 请求或响应标头执行操作。请访问 https://developer.mozilla.org/en-US/docs/Web/API/Headers 以获取详细信息以及使用它的示例。 (2认同)

Har*_*n O 17

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

前端或角度

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

后端(我使用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);
Run Code Online (Sandbox Code Playgroud)


Put*_*M S 13

添加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));
}
Run Code Online (Sandbox Code Playgroud)

  • 它给出错误“未捕获(承诺中)SyntaxError:输入意外结束” (9认同)

hoo*_*ogw 10

使用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               
Run Code Online (Sandbox Code Playgroud)


Ris*_*jan 9

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

  • 在UseRouting之前添加?你确定吗?所有其他参考资料都说相反,例如:[参见](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1#middleware-order-1) (2认同)

Yai*_*evy 8

只是我的两分钱...关于如何使用 CORS 代理来解决 \xe2\x80\x9cNo Access-Control-Allow-Originheader\xe2\x80\x9d 问题

\n

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

\n
    \n
  1. 创建一个名为“no-cors.php”的文件,其中包含以下内容:

    \n
    $URL = $_GET['url'];\necho json_encode(file_get_contents($URL));\ndie();\n
    Run Code Online (Sandbox Code Playgroud)\n
  2. \n
  3. 在你的前端,做类似的事情:

    \n
    fetch('https://example.com/no-cors.php' + '?url=' + url)\n  .then(response=>{*/Handle Response/*})`\n
    Run Code Online (Sandbox Code Playgroud)\n
  4. \n
\n


Uma*_*kar 7

CORS 问题的可能原因

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

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

    在此输入图像描述

  • 如果您正在使用该fetch方法并尝试访问跨域请求,请确保mode:cors存在。参考这个链接

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

  • 确保处理OPTIONAPI 中的方法。


fir*_*rst 6

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

const cors = require('cors');
app.use(cors({
    origin: 'http://example.com', // use your actual domain name (or localhost), using * is not recommended
    methods: ['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
}))
Run Code Online (Sandbox Code Playgroud)

现在,您可以从前端进行简单的 API 调用,而无需传递任何其他参数。


bri*_*web 5

在 2021 年 12 月,Chrome 97 中,除非在预检响应中(忽略) Authorization: Bearer ...,否则不允许使用。它产生了这个警告:Access-Control-Allow-Headers*

[Deprecation] authorization will not be covered by the wildcard symbol (*)
Run Code Online (Sandbox Code Playgroud)

请参阅:Chrome 企业版发行说明、Chrome 97

它似乎也对 实施同样的*限制Access-Control-Allow-Origin。如果您现在想恢复*类似行为,因为它已被阻止,您可能必须读取请求者的来源并将其作为预检响应中允许的来源返回。

Access-Control-Allow-Origin在某些情况下,当存在其他无效凭证(例如:过期的JWT )时,库可能会删除响应标头。然后,浏览器显示“不存在‘Access-Control-Allow-Origin’标头”错误,而不是实际错误(在本例中可能是过期的 JWT)。确保您的库不会丢失标头并使客户感到困惑。