【问题标题】:HTTP request from Angular sent as OPTIONS instead of POST来自 Angular 的 HTTP 请求作为 OPTIONS 而不是 POST 发送
【发布时间】:2017-03-22 16:45:43
【问题描述】:

我正在尝试从我的 angular.js 应用程序向我的服务器发送一些 HTTP 请求,但我需要解决一些 CORS 错误。

HTTP 请求是使用以下代码发出的:

functions.test = function(foo, bar) {
    return $http({
        method: 'POST',
        url: api_endpoint + 'test',
        headers: {
            'foo': 'value',
            'content-type': 'application/json'
        },
        data: {
            bar:'value'
        }
    });
};

第一次尝试以一些 CORS 错误告终。所以我在我的 PHP 脚本中添加了以下几行:

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT');
header('Access-Control-Allow-Headers: X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding, X-Auth-Token, content-type');

第一个错误现已消除。

现在 Chrome 的开发者控制台显示以下错误:

angular.js:12011 选项http://localhost:8000/test(匿名 函数)

423ef03a:1 XMLHttpRequest 无法加载 http://localhost:8000/test。预检响应包含无效的 HTTP 状态码 400

并且网络请求看起来像我预期的那样(HTTP 状态 400 也是预期的):

我无法想象如何解决这个问题(以及如何理解)为什么请求将在本地主机上以OPTIONS 发送并以POST 发送到远程服务器。有没有办法解决这个奇怪的问题?

【问题讨论】:

  • 尝试在标题中添加 'Accept': 'application/json'
  • 该 OPTIONS 是一个预检请求,由浏览器发送,因为在您的 POST 调用中设置了自定义标头;这是正常的。我认为问题在于这些标题的内容。
  • 问题是对预检 (OPTIONS) 请求的响应不成功,即 HTTP 400。这就是真正(后续)请求失败的原因。您必须确保对预检请求的响应是有效的。
  • 预期的HTTP状态码400是什么意思?
  • 你解决了吗?如果是的话,最好能描述一下为什么您的网络服务器或应用程序会为 OPTIONS 请求返回 HTTP 400 以及您是如何解决它的。例如,您可以为此创建另一个答案。

标签: javascript php angularjs http cors


【解决方案1】:

TL;DR 答案

说明

OPTIONS 请求被称为pre-flight request,它是Cross-origin resource sharing (CORS) 的一部分。浏览器使用它来检查是否允许来自特定域的请求,如下所示:

  1. 浏览器想要向特定的 URL 发送一个请求,比如一个 POST 请求,内容类型为 application/json
  2. 首先,它发送 pre-flight OPTIONS 请求到相同的 URL
  3. 以下内容取决于 pre-flight 请求的响应 HTTP 状态代码:
    • 如果服务器回复非2XX 状态响应,浏览器将不会发送实际请求(因为他现在知道无论如何都会被拒绝)
    • 如果服务器回复HTTP 200 OK(或任何其他2XX)响应,浏览器将发送实际请求,在您的情况下为POST

解决方案

因此,在您的情况下,存在正确的标头,您只需确保 pre-flight 请求的响应 HTTP 状态代码是 200 OK 或其他成功的( 2XX).


详细说明

Simple requests

浏览器在某些情况下发送飞行前请求,这些是所谓的简单请求,用于以下条件:

  • 允许的方法之一: - GET - HEAD - POST
  • 除了用户代理自动设置的标头(例如,Connection、User-Agent 等)外,唯一允许手动设置的标头如下:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type(但请注意以下附加要求)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • Content-Type 标头的唯一允许值是:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • 在请求中使用的任何XMLHttpRequestUpload 对象上都没有注册事件监听器;这些是使用XMLHttpRequest.upload 属性访问的。
  • 请求中未使用ReadableStream 对象。

这样的请求是直接发送的,服务器只是成功地处理了请求,或者在它不符合 CORS 规则的情况下回复错误。在任何情况下,响应都将包含 CORS 标头 Access-Control-Allow-*

Pre-flighted requests

如果实际请求不满足简单请求条件,浏览器正在发送飞行前请求,最常见的情况是:

  • 使用了application/xmlapplication/json 等自定义内容类型
  • 请求方法不是GETHEADPOST
  • POST 方法的内容类型不同于application/x-www-form-urlencodedmultipart/form-datatext/plain

您需要确保对飞行前请求的响应具有以下属性

  • 成功的HTTP状态码,即200 OK
  • header Access-Control-Allow-Origin: *(通配符*允许来自任何域的请求,当然你可以使用任何特定域来限制访问)

另一方面,服务器可以拒绝 CORS 请求,只需向 pre-flight 请求发送具有以下属性的响应:

  • 不成功的 HTTP 代码(即不是2XX
  • 成功的 HTTP 代码(例如 200 OK),但没有任何 CORS 标头(例如 Access-Control-Allow-*

详情请参阅documentation on Mozilla Developer NetworkHTML5Rocks' CORS tutorial

【讨论】:

  • 感谢您的清晰描述,解决了我的问题。
【解决方案2】:

我在编写 Angular 2 应用程序时遇到了一个非常相似的问题 - 该应用程序使用节点服务器作为 API。

由于我是在本地机器上开发的,所以当我尝试从我的 Angular 应用程序发布到 API 时,我不断遇到跨源头问题。

如下设置标头(在节点服务器中)适用于 GET 请求,但我的 PUT 请求不断将空对象发布到数据库。

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT');
header('Access-Control-Allow-Headers: X-Requested-With, Content-Type, 
Origin, Authorization, Accept, Client-Security-Token, Accept-
Encoding, X-Auth-Token, content-type');

阅读Dawid Ferenczy's post后,我意识到PREFLIGHT请求正在向我的服务器发送空白数据,这就是我的DB条目为空的原因,所以我在NODE JS服务器中添加了这一行:

  if (req.method == "OPTIONS")
    {
        res.writeHead(200, {"Content-Type": "application/json"});
        res.end();
    }

所以现在我的服务器忽略了 PREFLIGHT 请求,(并返回状态 200,让浏览器知道一切都很正常......)这样,真正的请求可以通过,我将真实的数据发布到我的数据库!

【讨论】:

  • 好吧,您并没有忽略飞行前的请求,而是正确地回复了它。传输任何数据不是飞行前请求的目的,其目的只是检查是否允许来自当前域的真实请求。
【解决方案3】:

随便放

if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header("HTTP/1.1 200 ");
exit;}

在您的服务器端应用程序开始时,您应该没问题。

【讨论】:

    【解决方案4】:

    对于 Spring Boot 应用程序,要启用 cors 请求,请在您各自的控制器上使用 @CrossOrigin(origins = "*", maxAge = 3600)

    Refer this doc

    【讨论】:

      【解决方案5】:

      从 Chrome v79+ 开始,OPTIONS Check(pre-flight request) 将不再出现在网络标签中-Source

      【讨论】:

        【解决方案6】:

        最好是:

        1. 设置 proxy.conf.json:

          {
            "/api": {
              "target": "http://localhost:8080",
              "secure": false,
              "logLevel": "debug",
              "changeOrigin": true
            }
          } 
          
        2. 然后确保您在 Angular 中用于发送请求的 URL 是relative (/api/something) 而不是absolute (localhost:8080/api/something)。因为在这种情况下代理将不起作用。

        【讨论】:

          猜你喜欢
          • 2014-04-16
          • 2015-07-27
          • 2016-06-14
          • 2014-02-08
          • 2018-04-15
          • 2018-08-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多