【问题标题】:Access-Control-Allow-Origin error but request goes throughAccess-Control-Allow-Origin 错误但请求通过
【发布时间】:2015-11-27 02:27:27
【问题描述】:

我目前正在为我的实时服务器部署一个基本 API,我遇到了(我认为是)CORS 问题,但发生了一些我无法解释的行为。

我正在从 AngularJS 前端与 Laravel 5 (+ laravel-cors) 后端进行通信。

我开始使用一个简单的 jQuery AJAX 调用(如下)进行测试,当我从本地 Vagrant 环境 (http://dev.example.local/test.html) 向http://api.example.com/v1/matches 发出请求时,我收到关于Access-Control-Allow-Origin 的错误。奇怪的是,请求确实通过了,因为信息通过基于 Laravel 的 API 正确存储在数据库中。

$.ajax({
    method: 'POST',
    url: 'http://api.example.com/v1/players',
    data: {
        "username": "username",
        "first_name": "First",
        "last_name": "Last",
        "nickname": ""
    }
}).always(function(r) {
    console.log(r);
});

错误:

XMLHttpRequest cannot load http://api.example.com/v1/players. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://other.example.com' is therefore not allowed access.

console.log(r) 返回{readyState: 0, responseJSON: undefined, status: 0, statusText: "error"}

我使用 Homestead VM (API) 和 Vagrant 环境(应用程序)在本地开发了应用程序,它在这些环境中正常工作...

一些观察:

  • 在我的 Chrome 开发者工具中,这些请求中的每一个都显示为方法:POST,状态:200 OK,类型:xhr
  • Postman 和 PhpStorm 的 RESTful 服务测试器等工具可以正确执行请求,并且添加数据时不会出错。

欢迎任何有关如何进一步调试此问题的想法...我一整天都在努力解决这个问题,但我只是不知道是什么原因造成的。

【问题讨论】:

    标签: angularjs laravel-5 cors


    【解决方案1】:

    您的服务器必须在响应中返回适当的 Access-Control-Allow-Origin 标头。例如,如果请求是从http://stackoverflow.com 发送的,那么您的服务器必须返回此标头:Access-Control-Allow-Origin: http://stackoverflow.com。在服务器端,您可以通过查看请求上的 Origin 标头来确定来源是什么。如果您的服务器在响应中返回此标头,则您将无权访问响应浏览器端的属性(例如状态代码、标头或消息正文)。 Same Origin Policy 位于此限制的中心。

    当 Postman 或 PhpStorm 的 RESTful 服务测试器发送请求时,您没有看到任何类似问题的原因是这些服务在请求中发送 Origin 标头,因为它们不受同源政策的约束。默认情况下,浏览器会将此标头附加到任何跨域 ajax 请求,因为浏览器 受同源策略的约束。在我之前的场景中,请求标头如下所示:Origin: http://stackoverflow.com。实现the CORS spec 的浏览器需要添加此请求标头,以便服务器能够确定请求的来源是否已被列入跨域ajax 请求的白名单。如果是这种情况,服务器将返回正确的Access-Control-Allow-Origin 标头。如果没有,它可以简单地省略标题。没有实现 CORS 规范的浏览器只会拒绝发送这样的 ajax 请求。

    关于您对为什么首先发送请求感到困惑,这归结为“简单”和“非简单”CORS 请求之间的区别。对于简单的 CORS 请求,请求将始终发送到服务器,但客户端/JS 将无法在没有服务器正确确认的情况下解析响应。可以这么说,一些 CORS 请求简单。例如,DELETEPATCH 请求,或包含非标准标头的 POST/GET 请求(例如 X 标头或“application/json”的 Content-Type,而不是“多部分/表单数据”)。换句话说,如果没有 JavaScript 就无法发送请求,那么它不是简单的。例如,<form> 提交或来自<script src="..."> 的 GET 请求将始终发送“简单”请求。对于非简单请求,浏览器必须“预检”请求。这意味着浏览器发送一个中间请求,称为预检,原始请求之前。此预检请求是 OPTIONS 请求。然后,服务器必须在对此预检的响应中返回标头,以确认原始请求的任何非标准属性。如果是,则浏览器将发送原始请求。

    您可以在MDN 上阅读有关预检和 CORS 的更多信息。

    【讨论】:

    • 我已经知道你所说的大部分内容,但对为什么请求会通过的解释对我来说是新的,谢谢!我 99% 确定后端将 Access-Control-Allow-Origin 设置为 *,因为它是一个公共 API,有什么方法可以测试吗?而且由于我通过 jQuery 运行 AJAX POST 请求,它们并不简单,对吧?所以他们应该发送预检请求。我应该能够在 Chrome devtools 的 de Network 选项卡中看到这个吗,因为我没有看到任何内容。
    • 原来我错误地实现了 laravel-cors 包之类的。我将 Access-Control-Allow-Origin 标头添加到我的 apache2 虚拟主机中,现在它可以正常工作了...
    • 如果浏览器没有发送预检,那么请求是“简单的”。无论您是否使用 jQuery 发送它们都没有区别。只有当请求包含我在回答中提到的一些内容时,请求才是不简单的。例如,如果您将 jQuery 发送的请求的内容类型设置为 application/json,则浏览器会预检该请求。我链接到的 MDN 文章更详细。
    • 您的问题并不明显,您了解 CORS 是什么,或者您已经尝试返回正确的 CORS 标头。我现在看到你提到了 laraval-cors。
    • 完全没问题。感谢您的详细解释,它确实刷新了我对 CORS 的记忆。由于您的回答,我最终对其进行了更多测试,因此我将其标记为已接受。
    猜你喜欢
    • 1970-01-01
    • 2017-08-23
    • 2016-11-04
    • 2018-04-16
    • 2016-08-03
    • 1970-01-01
    • 1970-01-01
    • 2017-12-16
    • 2018-12-12
    相关资源
    最近更新 更多