jQuery $.ajax(), $.post sending “OPTIONS” as REQUEST_METHOD in Firefox
遇到了我认为相对简单的jquery插件的问题…
插件应该通过Ajax从PHP脚本中获取数据,以向
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $.ajax({ url: o.url, type: 'post', contentType:"application/x-www-form-urlencoded", data: '{"method":"getStates","program":"EXPLORE"}', success: function (data, status) { console.log("Success!!"); console.log(data); console.log(status); }, error: function (xhr, desc, err) { console.log(xhr); console.log("Desc:" + desc +" Err:" + err); } }); |
这在狩猎中似乎很管用。在firefox 3.5中,服务器上的
1 | ::1 - - [08/Jul/2009:11:43:27 -0500]"OPTIONS sitecodes.php HTTP/1.1" 200 46 |
为什么这个Ajax调用可以在Safari中工作,而不是在Firefox中工作,我该如何修复它呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | Response Headers Date: Wed, 08 Jul 2009 21:22:17 GMT Server:Apache/2.0.59 (Unix) PHP/5.2.6 DAV/2 X-Powered-By: PHP/5.2.6 Content-Length 46 Keep-Alive timeout=15, max=100 Connection Keep-Alive Content-Type text/html Request Headers Host orderform:8888 User-Agent Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1) Gecko/20090624 Firefox/3.5 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language en-us,en;q=0.5 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive 300 Connection keep-alive Origin http://ux.inetu.act.org Access-Control-Request-Method POST Access-Control-Request-Headers x-requested-with |
以下是Firebug输出的图片:
错误的原因是同一源策略。它只允许您对自己的域执行xmlhttprequest。查看是否可以使用JSONP回调:
1 | $.getJSON( 'http://<url>/api.php?callback=?', function ( data ) { alert ( data ); } ); |
我在Django端使用以下代码来解释选项请求并设置所需的访问控制头。在这之后,来自Firefox的跨域请求开始工作。如前所述,浏览器首先发送选项请求,然后在发送后立即发送post/get
1 2 3 4 5 6 7 8 9 10 11 | def send_data(request): if request.method =="OPTIONS": response = HttpResponse() response['Access-Control-Allow-Origin'] = '*' response['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS' response['Access-Control-Max-Age'] = 1000 # note that '*' is not valid for Access-Control-Allow-Headers response['Access-Control-Allow-Headers'] = 'origin, x-csrftoken, content-type, accept' return response if request.method =="POST": # ... |
编辑:看起来至少在某些情况下,您还需要向实际响应添加相同的访问控制头。这可能有点令人困惑,因为请求似乎成功了,但火狐没有将响应的内容传递给javascript。
这篇Mozilla开发者中心文章描述了各种跨域请求场景。文章似乎表明,内容类型为"application/x-www-form-urlencoded"的POST请求应作为"简单请求"(没有"preflight"选项请求)发送。不过,我发现火狐发送了选项请求,即使我的帖子是用这种内容类型发送的。
我可以通过在服务器上创建一个选项请求处理程序来实现这一点,该处理程序将"access control allow origin"响应头设置为"*"。通过将其设置为特定的内容,例如"http://someurl.com",可以更严格地限制它。另外,我已经读过了,假设您可以指定一个多个起源的逗号分隔列表,但是我不能让它起作用。
一旦firefox接收到带有可接受的"访问控制允许来源"值的选项请求响应,它就会发送post请求。
我已经使用一个完全基于Apache的解决方案解决了这个问题。在我的vhost/htaccess中,我放置了以下块:
1 2 3 4 5 6 7 8 | # enable cross domain access control Header always set Access-Control-Allow-Origin"*" Header always set Access-Control-Allow-Methods"POST, GET, OPTIONS" # force apache to return 200 without executing my scripts RewriteEngine On RewriteCond %{REQUEST_METHOD} OPTIONS RewriteRule .* / [R=200,L] |
您可能不需要后一部分,这取决于Apache执行目标脚本时会发生什么。后一部分归功于友好的serverfault-folk。
这个位于响应脚本顶部的PHP似乎可以工作。(使用火狐3.6.11。我还没有做过很多测试。)
1 2 3 4 5 6 7 8 9 10 11 12 13 | header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: POST, GET, OPTIONS'); header('Access-Control-Max-Age: 1000'); if(array_key_exists('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', $_SERVER)) { header('Access-Control-Allow-Headers: ' . $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']); } else { header('Access-Control-Allow-Headers: *'); } if("OPTIONS" == $_SERVER['REQUEST_METHOD']) { exit(0); } |
我在向谷歌地图发送请求时遇到了同样的问题,jquery 1.5的解决方案非常简单——对于数据类型,使用
罪魁祸首是使用选项方法的飞行前请求
对于可能对用户数据产生副作用的HTTP请求方法(特别是对于GET以外的HTTP方法,或某些MIME类型的后期使用),规范要求浏览器"预先处理"请求,使用HTTP选项请求方法从服务器请求支持的方法,然后在服务器"批准"后,使用实际HTTP请求方法发送实际请求。
Web规范请参阅:https://developer.mozilla.org/en-us/docs/web/http/access_control_cors
我通过在nginx conf中添加以下行解决了这个问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | location / { if ($request_method = OPTIONS ) { add_header Access-Control-Allow-Origin "*"; add_header Access-Control-Allow-Methods"POST, GET, PUT, UPDATE, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers"Authorization"; add_header Access-Control-Allow-Credentials "true"; add_header Content-Length 0; add_header Content-Type text/plain; return 200; } location ~ ^/(xxxx)$ { if ($request_method = OPTIONS) { rewrite ^(.*)$ / last; } } |
我在查看源代码1.3.2时,使用JSONP时,请求是通过动态构建脚本元素发出的,该元素通过浏览器的相同域策略。当然,您不能使用脚本元素发出post请求,浏览器将使用get获取结果。
当您请求JSONP调用时,不会生成脚本元素,因为它只在Ajax调用类型设置为get时才这样做。
http://dev.jquery.com/ticket/4690
我们在ASP.NET中遇到了这样的问题。由于pagehandlerFactory被限制只响应
您可以在IIS管理器中修改它,选择您的网站,然后选择处理程序映射,根据需要双击pagehandlerFactory中的*.apx文件(我们使用带框架4.0的集成应用程序池)。单击请求限制,然后转到谓词选项卡并应用修改。
现在我们的
检查您的表单的
通常对规范URL执行。
我苦苦挣扎了几个小时,才跌跌撞撞地读到这篇文章,发现了跨领域的暗示。
我觉得如果
如果执行post请求,为什么不直接使用$.post方法:
1 2 3 4 5 | $.post("test.php", { func:"getNameAndTime" }, function(data){ alert(data.name); // John console.log(data.time); // 2pm },"json"); |
简单多了。
解决方法是:
这对调用Facebook API和Firefox很有效。Firebug在上述条件下使用的是
另一种规避问题的方法是使用代理脚本。例如,这里描述了该方法
我已经发布了一个清晰的示例,说明了如果控制要发布到的域的服务器代码,如何解决这个问题。这个答案在这条线索中被提及,但这更清楚地解释了这一点。
如何通过javascript发送跨域的POST请求?
您需要在服务器端做一些工作。我看到您在服务器端使用了PHP,但是.NET Web应用程序的解决方案如下:无法在jquery.ajax中将内容类型设置为"application/json"
在PHP脚本中执行同样的操作,它将工作。简单:第一个请求浏览器询问服务器是否允许使用此类类型发送此类数据,第二个请求是正确的/允许的。
尝试添加以下内容:
1 2 3 | dataType:"json", ContentType:"application/json", data: JSON.stringify({"method":"getStates","program":"EXPLORE"}), |
我已经有了这个代码,可以很好地处理我在PHP中的CORS情况:
1 2 3 | header( 'Access-Control-Allow-Origin: '.CMSConfig::ALLOW_DOMAIN ); header( 'Access-Control-Allow-Headers: '.CMSConfig::ALLOW_DOMAIN ); header( 'Access-Control-Allow-Credentials: true' ); |
而且它在本地和远程都可以正常工作,但在远程时不适用于上传。
Apache/PHP或我的代码发生了一些问题,我不想搜索它,当您请求选项时,它用CORS规则返回我的头,但得到302个结果。因此,我的浏览器无法识别为可接受的情况。
我所做的,基于@mark mcdonald answer,只是把这个代码放在我的头后面:
1 2 3 4 5 | if( $_SERVER['REQUEST_METHOD'] === 'OPTIONS' ) { header("HTTP/1.1 202 Accepted"); exit; } |
现在,当请求EDOCX1[4]时,它只发送头和202结果。
我在尝试使用Facebook API时遇到了类似的问题。
唯一没有发送预先照亮的请求的ContentType似乎只是文本/纯文本…这里不是Mozilla提到的其他参数
- 为什么这是唯一一个这样做的浏览器?
- 为什么Facebook不知道并接受飞行前的请求?
仅供参考:前面提到的moz-doc建议x-lori头应该触发一个预先激活的请求…没有。
尝试添加选项:
数据类型:"json"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | function test_success(page,name,id,divname,str) { var dropdownIndex = document.getElementById(name).selectedIndex; var dropdownValue = document.getElementById(name)[dropdownIndex].value; var params='&'+id+'='+dropdownValue+'&'+str; //makerequest_sp(url, params, divid1); $.ajax({ url: page, type:"post", data: params, // callback handler that will be called on success success: function(response, textStatus, jqXHR){ // log a message to the console document.getElementById(divname).innerHTML = response; var retname = 'n_district'; var dropdownIndex = document.getElementById(retname).selectedIndex; var dropdownValue = document.getElementById(retname)[dropdownIndex].value; if(dropdownValue >0) { //alert(dropdownValue); document.getElementById('inputname').value = dropdownValue; } else { document.getElementById('inputname').value ="00"; } return; url2=page2; var params2 = parrams2+'&'; makerequest_sp(url2, params2, divid2); } }); } |
你能试试这个吗
当我想将数据发布到托管在另一台服务器上的ApacheSolr时,我使用代理URL来解决类似的问题。(这也许不是完美的答案,但它解决了我的问题。)
遵循此URL:使用模式重写进行代理,我将此行添加到httpd.conf:
1 | RewriteRule ^solr/(.*)$ http://ip:8983/solr$1 [P] |
因此,我可以将数据发布到/solr,而不是将数据发布到http://ip:8983/solr/*。然后它将以相同的来源发布数据。
请注意:
JSONP只支持GET请求方法。
*通过Firefox发送请求:*
1 2 3 4 5 6 7 | $.ajax({ type: 'POST',//<<=== contentType: 'application/json', url: url, dataType:"json"//<<============= ... }); |
以上请求按选项发送(while==>type:'post')!!!!!
1 2 3 4 5 6 7 | $.ajax({ type: 'POST',//<<=== contentType: 'application/json', url: url, dataType:"jsonp"//<<============== ... }); |
但是上面的请求是通过get发送的(while=>type:'post')!!!!!
当你在"跨领域交流"时,注意并小心。