Open karchinkong opened 6 years ago
最近项目碰到了跨域请求的问题,项目地址是 a.example.com,而请求的 cgi 地址是 b.example.com。第一次实际处理跨域请求问题,还是学习到不少的东西,查阅了不少,遂趁机整理一番。
XMLHttpRequest 说到跨域,还是得区分浏览器来说。先讲讲 chrome 等主流浏览器,包括 IE10+。这些浏览器可以使用 CORS 来实现 ajax 跨域请求。使用 jquery ajax 跨域请求时,会发现没有带上 cookies,这时候需要设置 crossDomain 和 withCredentials:
$.ajaxSetup({ crossDomain: true, xhrFields: { withCredentials: true } });
$.ajax({ type : 'post', url : 'http://b.example.com/cgi/xxoo', // ... }); 同时需要后端 PHP 配合设置:
<?php $url = 'http://a.example.com'; header('Access-Control-Allow-Origin: ' . $url); header('Access-Control-Allow-Credentials:true'); ?> 详情参考阮一峰老师的《跨域资源共享 CORS 详解》。
jquery ajax 实际上就是 XMLHttpRequest 请求,IE8 与 IE9 并不支持,而它们可以使用 XDomainRequest 来实现。具体实现可参考 StackOverflow 上的两个问答:
http://stackoverflow.com/questions/24206782/ajax-call-not-working-in-ie8 http://stackoverflow.com/questions/3362474/jquery-ajax-fails-in-ie-on-cross-domain-calls#11267937 jquery 插件 嫌麻烦了,正好有 jquey 插件 jQuery-ajaxTransport-XDomainRequest 帮忙实现了,不过它有几点需要注意的:
只能是 GET 或者是 POST 请求; POST 请求的数据类型必须是 text/plain; 只支持 HTTP 和 HTTPS 协议。 但是!但是!但是!作者都说了,它不能带上 cookies 呀,那就完全不能用了啊......
jsonp 的原理是通过添加一个 script 标签,然后向服务器请求 JSON 数据。服务器收到请求后,将数据放到一个指定的回调函数传回来。
优点:兼容所有老式浏览器。 缺点:只支持 GET 请求,同时需要后端 PHP 支持数据格式传回。
jquery 代码如下:
$.ajax({ type : 'post', url : 'http://b.example.com/cgi/xxoo', dataType : 'jsonp', jsonp: 'callback', //传递给请求处理程序或页面的,用以获得 jsonp 回调函数名的参数名(默认为: callback) jsonpCallback:'success_jsonpCallback', //自定义的 jsonp 回调函数名称,默认为 jQuery 自动生成的随机函数名 success: function(json) { alert('success'); }, error: function() { alert('fail'); } });
PHP 代码如下:
<?php $data = '.......'; $callback = $_GET['callback']; echo $callback . '(' . json_encode($data) . ')'; exit; ?>
form + iframe 提交 有一些请求还是得使用 POST 请求,怎么办......
那就不能用 ajax 请求了,得用老式方法 form 表单提交,因为它能在提交时会带上 cookies。
但是 form 表单提交会刷新整个页面,这就尴尬了。还好可以设置 form 的 target 属性到一个空的 iframe,表示在 iframe 里面刷新页面,然后把 iframe 隐藏起来,看起来就像是页面异步请求了。
还有需将页面和 iframe 设置成同一个大域,即:
document.domain = 'example.com'; 如果是两个完全不同的域,可以使用 window.name 或 window.postMessage 来实现,主要是要让页面能拿到 iframe 中的返回数据。
form 需将 target 指向 iframe,来实现本页面不会刷新的效果。 html 代码如下:
<form id="myForm" method="post" target="myFrame"> <!-- .... --> </form> <iframe name="myFrame" style="display:none;"></iframe>
然后接口是 form 表单提交,这里需要全局添加一个 callback 方法。可以把 callback 名字传给后端让它回调,这里跟后端 统一后先写死。js 代码如下:
var $form = $('#myForm'); $form.attr('action', 'http://b.example.com/cig/xxoo'); $form.submit(); window.callback = function(json) { console.log(json); window.callback = null; }
后端需设置 document.domian,然后 $callback 与前端统一好,最后返回一段 script 标签。php 代码如下:
<?php header('Content-type:text/html;charset-utf-8'); $callback = 'callback'; $args = array( 'name' => 'cobish', 'age' => 24 ); $params = json_encode($args); echo '<script>document.domain = 'example.com';parent.' . $callback . '(' . $params . ')' . '</script>'; ?>
跨域请求接口得视浏览器兼容而定,如果只需兼容主流浏览器,那么可以使用 CORS。如果需兼容 IE8 或 IE9,则 GET 请求可以使用 JSONP,POST 请求可以使用 form + iframe。
三种方法都需要后端的支持。
CORS 需后端设置 Access-Control-Allow-Origin 和 Access-Control-Allow-Credentials 来允许前端访问; JSON 则需后端返回执行的 js 回调函数; form + iframe 则需后端返回一段 script 便签,里面包含 document.domian 设置和执行回调函数。 跨域的解决方案参考《前端跨域问题及解决方案》。
最近项目碰到了跨域请求的问题,项目地址是 a.example.com,而请求的 cgi 地址是 b.example.com。第一次实际处理跨域请求问题,还是学习到不少的东西,查阅了不少,遂趁机整理一番。
CORS
XMLHttpRequest 说到跨域,还是得区分浏览器来说。先讲讲 chrome 等主流浏览器,包括 IE10+。这些浏览器可以使用 CORS 来实现 ajax 跨域请求。使用 jquery ajax 跨域请求时,会发现没有带上 cookies,这时候需要设置 crossDomain 和 withCredentials:
$.ajaxSetup({ crossDomain: true, xhrFields: { withCredentials: true } });
$.ajax({ type : 'post', url : 'http://b.example.com/cgi/xxoo', // ... }); 同时需要后端 PHP 配合设置:
<?php $url = 'http://a.example.com'; header('Access-Control-Allow-Origin: ' . $url); header('Access-Control-Allow-Credentials:true'); ?> 详情参考阮一峰老师的《跨域资源共享 CORS 详解》。
XDomainRequest
jquery ajax 实际上就是 XMLHttpRequest 请求,IE8 与 IE9 并不支持,而它们可以使用 XDomainRequest 来实现。具体实现可参考 StackOverflow 上的两个问答:
http://stackoverflow.com/questions/24206782/ajax-call-not-working-in-ie8 http://stackoverflow.com/questions/3362474/jquery-ajax-fails-in-ie-on-cross-domain-calls#11267937 jquery 插件 嫌麻烦了,正好有 jquey 插件 jQuery-ajaxTransport-XDomainRequest 帮忙实现了,不过它有几点需要注意的:
只能是 GET 或者是 POST 请求; POST 请求的数据类型必须是 text/plain; 只支持 HTTP 和 HTTPS 协议。 但是!但是!但是!作者都说了,它不能带上 cookies 呀,那就完全不能用了啊......
jsonp
jsonp 的原理是通过添加一个 script 标签,然后向服务器请求 JSON 数据。服务器收到请求后,将数据放到一个指定的回调函数传回来。
优点:兼容所有老式浏览器。 缺点:只支持 GET 请求,同时需要后端 PHP 支持数据格式传回。
jquery 代码如下:
PHP 代码如下:
form + iframe 提交 有一些请求还是得使用 POST 请求,怎么办......
那就不能用 ajax 请求了,得用老式方法 form 表单提交,因为它能在提交时会带上 cookies。
但是 form 表单提交会刷新整个页面,这就尴尬了。还好可以设置 form 的 target 属性到一个空的 iframe,表示在 iframe 里面刷新页面,然后把 iframe 隐藏起来,看起来就像是页面异步请求了。
还有需将页面和 iframe 设置成同一个大域,即:
document.domain = 'example.com'; 如果是两个完全不同的域,可以使用 window.name 或 window.postMessage 来实现,主要是要让页面能拿到 iframe 中的返回数据。
form 需将 target 指向 iframe,来实现本页面不会刷新的效果。 html 代码如下:
然后接口是 form 表单提交,这里需要全局添加一个 callback 方法。可以把 callback 名字传给后端让它回调,这里跟后端 统一后先写死。js 代码如下:
后端需设置 document.domian,然后 $callback 与前端统一好,最后返回一段 script 标签。php 代码如下:
总结
跨域请求接口得视浏览器兼容而定,如果只需兼容主流浏览器,那么可以使用 CORS。如果需兼容 IE8 或 IE9,则 GET 请求可以使用 JSONP,POST 请求可以使用 form + iframe。
三种方法都需要后端的支持。
CORS 需后端设置 Access-Control-Allow-Origin 和 Access-Control-Allow-Credentials 来允许前端访问; JSON 则需后端返回执行的 js 回调函数; form + iframe 则需后端返回一段 script 便签,里面包含 document.domian 设置和执行回调函数。 跨域的解决方案参考《前端跨域问题及解决方案》。