如何跳过“选项”预检请求?


92

我开发了一个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。

提前致谢。

Answers:


100

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

如果进行此更改后仍然看到预检,则Angular可能还会在请求中添加X标头。

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


9
这是正确的答案-您的Content-Type和Cache-Control标头正在触发预检请求。具有文本/纯文本的Content-Type的普通GET和其他几种是触发非预检请求的唯一方法。请参阅:developer.mozilla.org/en-US/docs/HTTP/...
杰夫·哈伯德

是的,忘记了Cache-Control。
Ray Nicholus 2014年

15
自定义标题也会触发预检。
塞巴斯蒂安Deprez

1
不能通过更改内容类型来防止OPTIONs测试。内容类型应与内容类型匹配,无论如何
ekerner

如果是浏览器抛出异常,并且在后端,则Http方法OPTIONS被阻止,是否会产生任何效果,例如由于OPTIONS失败,浏览器将不会为POST / PUT调用相应的API?
P Satish Patro

16

正如Ray所说,您可以通过修改内容标题来停止它,例如-

 $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(){...});

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

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


1
我应该怎么做呢$http
Learnercys

谢谢,这与我正在做的类似。唯一的变化是方法GET和额外的标题Authorization。但是仍然发送预检。
Learnercys

1
您可以在此处粘贴您的请求吗?如卷曲或其他?可能是由于Authorization标头所致,请尝试将其删除然后再尝试。如果您要发送自定义标题,则angular将发送飞行前请求。
vivex

pastebin.com/vRDeFiH2第一个是对$ http的请求,第二个是由邮递员构建的有效请求:)
Learnercys

2

当执行某些类型的跨域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

2

我认为最好的方法是检查请求是否为“ OPTIONS”类型,并从中间件返回200。它为我工作。

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

它可以工作,但是在OWASP中,建议不要公开OPTIONS。你天气阻止它在后台/托管服务(Nginx的,Apache的)等
P萨蒂什Patro

0

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

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


您真的不能指望OP会告诉他的客户关闭浏览器安全性只是为了启用一项功能,对吗?
svarog

@svarog这主要是出于开发目的,主要是在生产服务器上,您将不会遇到此问题。
Arijit Patra

如果是浏览器抛出异常,并且在后端,则Http方法OPTIONS被阻止,是否会产生任何效果,例如由于OPTIONS失败,浏览器将不会为POST / PUT调用相应的API?
P Satish Patro

-2

将content-type设置为undefined将使javascript传递标头数据,因为它是,并覆盖默认的有角度的$ httpProvider标头配置。Angular $ http文档

$http({url:url,method:"POST", headers:{'Content-Type':undefined}).then(success,failure);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.