【问题标题】:How to skip the OPTIONS preflight request?如何跳过 OPTIONS 预检请求?
【发布时间】:2014-05-23 00:01:18
【问题描述】:

我开发了一个 PhoneGap 应用程序,现在它正在转变为一个移动网站。除了一个小故障外,一切都很顺利。我通过 POST 请求使用某个第三方 API,该 API 在应用程序中运行良好,但在移动网站版本中失败。

仔细一看,AngularJS(我猜实际上是浏览器)似乎首先发送了一个 OPTIONS 请求。我今天学到了很多关于 CORS 的知识,但我似乎无法弄清楚如何完全禁用它。我没有访问该 API 的权限(因此无法在那边进行更改),但他们已将我正在处理的域添加到他们的 Access-Control-Allow-Origin 标头中。

这是我正在谈论的代码:

        var request = {
                language: 'fr',
                barcodes: [
                    {
                        barcode: 'somebarcode',
                        description: 'Description goes here'
                    }
                ]
            };
        }
        var config = {
            headers: { 
                'Cache-Control': 'no-cache',
                'Content-Type': 'application/json'
            }
        };
        $http.post('http://somedomain.be/trackinginfo', request, config).success(function(data, status) {
            callback(undefined, data);
        }).error(function(data, status) {
            var err = new Error('Error message');
            err.status = status;
            callback(err);
        });

如何防止浏览器(或 AngularJS)发送 OPTIONS 请求而直接跳到实际的 POST 请求?我正在使用 AngularJS 1.2.0。

提前致谢。

【问题讨论】:

    标签: angularjs post cors preflight


    【解决方案1】:

    预检是由 application/json 的 Content-Type 触发的。防止这种情况的最简单方法是在您的情况下将 Content-Type 设置为 text/plainapplication/x-www-form-urlencoded & multipart/form-data Content-Types 也是可以接受的,但您当然需要适当地格式化您的请求负载。

    如果您在进行此更改后仍然看到预检,那么 Angular 可能也在向请求中添加 X-header。

    或者您可能有会触发它的标头(授权、缓存控制...),请参阅:

    【讨论】:

    • 这是正确的答案——您的 Content-Type 和 Cache-Control 标头正在触发预检请求。 Content-Type 为 text/plain 的普通 GET 和其他一些是触发非预检请求的唯一方法。见:developer.mozilla.org/en-US/docs/HTTP/…
    • 啊,是的,忘了缓存控制。
    • 自定义标头也会触发预检。
    • 更改内容类型以防止 OPTIONs 测试不是答案。内容类型应与内容类型匹配
    • 如果是浏览器抛出,&在后端,Http方法OPTIONS被阻塞,会不会像OPTIONS失败,浏览器不会调用POST/PUT对应的API一样有什么影响?跨度>
    【解决方案2】:

    正如雷所说,您可以通过修改 content-header 来阻止它 -

     $http.defaults.headers.post["Content-Type"] = "text/plain";
    

    例如 -

    angular.module('myApp').factory('User', ['$resource','$http',
        function($resource,$http){
            $http.defaults.headers.post["Content-Type"] = "text/plain";
            return $resource(API_ENGINE_URL+'user/:userId', {}, {
                query: {method:'GET', params:{userId:'users'}, isArray:true},
                getLoggedIn:{method:'GET'}
            });
        }]);
    

    或者直接来个电话-

    var req = {
     method: 'POST',
     url: 'http://example.com',
     headers: {
       'Content-Type': 'text/plain'
     },
     data: { test: 'test' }
    }
    
    $http(req).then(function(){...}, function(){...});
    

    这不会发送任何飞行前选项请求。

    注意:请求不应有任何自定义头参数,如果请求头包含任何自定义头,则浏览器将发出飞行前请求,您无法避免。

    【讨论】:

    • 我应该如何使用$http
    • 谢谢,这与我所做的类似。唯一的变化是方法GET 和一个额外的标题Authorization。但仍在发送预检。
    • 您可以在这里粘贴您的请求吗?作为卷曲什么的?也许是因为 Authorization 标头,尝试将其删除然后尝试。如果您要发送自定义标头,则 Angular 将发送飞行前请求。
    • pastebin.com/vRDeFiH2 第一个是对 $http 的请求,第二个是邮递员构建的有效请求 :)
    【解决方案3】:

    在执行某些类型的跨域 AJAX 请求时,支持 CORS 的现代浏览器会插入一个额外的“预检”请求,以确定它们是否有权执行该操作。 来自示例查询:

    $http.get( ‘https://example.com/api/v1/users/’ +userId,
      {params:{
               apiKey:’34d1e55e4b02e56a67b0b66’
              }
      } 
    );
    

    作为这个片段的结果,我们可以看到该地址被发送了两个请求(OPTIONS 和 GET)。来自服务器的响应包括确认查询 GET 允许性的标头。如果您的服务器未配置为正确处理 OPTIONS 请求,则客户端请求将失败。例如:

    Access-Control-Allow-Credentials: true
    Access-Control-Allow-Headers: accept, origin, x-requested-with, content-type
    Access-Control-Allow-Methods: DELETE
    Access-Control-Allow-Methods: OPTIONS
    Access-Control-Allow-Methods: PUT
    Access-Control-Allow-Methods: GET
    Access-Control-Allow-Methods: POST
    Access-Control-Allow-Orgin: *
    Access-Control-Max-Age: 172800
    Allow: PUT
    Allow: OPTIONS
    Allow: POST
    Allow: DELETE
    Allow: GET
    

    【讨论】:

      【解决方案4】:

      我认为最好的方法是检查请求是否属于“OPTIONS”类型,从中间件返回 200。它对我有用。

      express.use('*',(req,res,next) =>{
            if (req.method == "OPTIONS") {
              res.status(200);
              res.send();
            }else{
              next();
            }
          });
      

      【讨论】:

      • 它可以工作,但在 OWASP 中建议不要公开 OPTIONS。你在后端/托管服务(Nginx,Apache)等中阻止它。
      【解决方案5】:

      预检是浏览器实现的网络安全功能。对于 Chrome,您可以通过添加 --disable-web-security 标志来禁用所有网络安全。

      例如: "C:\Program Files\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir="C:\newChromeSettingsWithoutSecurity" 。您可以首先创建一个新的chrome快捷方式,转到其属性并如上所述更改目标。这应该有帮助!

      【讨论】:

      • 你不能指望 OP 告诉他的客户关闭浏览器安全性只是为了启用一项功能,对吧?!
      • @svarog 这主要用于开发目的,主要是在生产服务器上你不会遇到这个问题。
      • 如果是浏览器抛出,&在后端,Http方法OPTIONS被阻塞,会不会像OPTIONS失败,浏览器不会调用POST/PUT对应的API一样有什么影响?跨度>
      【解决方案6】:

      将 content-type 设置为 undefined 将使 javascript 按原样传递标头数据,并覆盖默认的角度 $httpProvider 标头配置。 Angular $http Documentation

      $http({url:url,method:"POST", headers:{'Content-Type':undefined}).then(success,failure);
      

      【讨论】:

        猜你喜欢
        • 2012-11-24
        • 2015-08-04
        • 2015-09-01
        • 1970-01-01
        • 2014-03-14
        • 2011-03-30
        • 2014-09-03
        • 2019-10-03
        • 2018-01-13
        相关资源
        最近更新 更多