对于跨域的GET请求,我们最常用的是jsonp的方式,jQuery的ajax方法也对jsonp也有很好的封装,我们甚至可以利用http.getJSONP(url,data,callback)这样简洁的方式让开发人员只关注请求的url,数据以及回调方法。但是如果传输的数据量比较大,或者数据信息比较敏感的话,则需要POST大神出手了。那么跨域的post请求是否也能做到如此优雅地调用方式呢?
现在假设在a.com有个login.html页面,我们要用户的用户名密码提交到b.com/post接口做校验,校验结果返回a.com/login.html做提示。我们也来打造这样一个方法:
/* 跨域post请求方法 * url 请求地址 * data post表单数据 * callback 回调方法 */ http.postJSONP = function(url,callback) { //TO DO } //请求方式 http.postJSONP('b.com/post',{ name: "garygao",password: "******" },function(json) { //TO DO json }); //或者像jsonp一样 http.postJSONP('b.com/post?postcallback=xxoo',password: "******" }); //postcallback回调方法 function xxoo(json) { //TO DO json }
要实现这样一个方法,我们有两个难点:
1、如何实现跨域的post请求?
2、如何捕获回调数据,优雅地执行?
如何实现跨域POST请求?
1、CORS
利用CORS,让目标服务器设置标头"Access-Control-Allow-Origin:xxx.com ",表示允许设定的域向我们的服务端提交请求。
优点:W3C标准,配置简单;
缺点:需要服务端配合,IE67不支持。
2、Server Proxy
当前域实现一个代理,所有向外部域名发送的请求都径由该代理中转。
缺点:每个使用方都需要部署代理,数据中转低效,对js有侵入。
3、Flash Proxy
服务端部署跨域策略文件crossdomain.xml,页面利用不可见的swf跨域post提交数据实现跨域通信。
优点:兼容性好,传输数据量大;
缺点:依赖flash。
4、Invisible Iframe
概述:通过js动态生成不可见表单和iframe,将表单的target设为iframe的name以此通过iframe做post提交。
优点:兼容性佳,原生JS即可完全实现;
缺点:无法直接读取响应内容。
综合对比四种方式,还是第四种比较给力,兼容性好,不需要做服务器配置也不依赖Flash文件;
注:需要应用页面、proxy.html、以及后台返回的数据需要<script>包住window.name的赋值,虽然很奇怪,但是这样也是为了让前端容易获取到后台返回的数据。
我在本地进行测试的时候,一个服务器配置了两个域名,这样就可实现跨域访问,比如在hosts中这样写:
127.0.0.1 a.test.com
127.0.0.1 b.test.com
在文件夹中建两个文件夹cross_domain_a和cross_domain_b ,前者中放应用页面(请求的页面)和proxy.html,后者放后台的程序即返回给前台的数据。另proxy.html是一个空白页面。原理:
利用window.name可以跨域访问,且值会保持不变,在前台中用脚本生成一个form表单和iframe,form.target= iframe.name,form.action = url(此url就是后台提供的接口),这样提交到后台的数据后台可以接收到,另后台的返回的数据也保存在了window.name中,且form提交完成之后,将location置为同域,这样就间接的解决掉了跨域的问题。
下面是前端js代码:
var http = {}; http.postJSONP = function(url,fn) { var form = document.createElement("form"); form.id = form.name = 'postForm'; //创建表单数据 if (data) { for(var key in data) { var input = document.createElement("input"); input.type = "hidden"; input.name = key; input.value = data[key]; form.appendChild(input); } } //创建iframe var iframe = null; //try&catch是为了解决IE67创建iframe新开窗问题 try { iframe = document.createElement('<iframe name="postIframe">'); } catch (ex) { iframe = document.createElement('iframe'); } iframe.id = iframe.name = "postIframe"; iframe.width = "1"; iframe.height = "1"; iframe.style.display = "none"; document.body.appendChild(iframe); //表单提交 document.body.appendChild(form); form.action = url; form.target = iframe.name; //把表单的target设为iframe的name form.method = "post"; form.submit(); //iframe.src='http:' //事件处理 if(iframe.attachEvent){ iframe.attachEvent("onload",_loadFn); }else{ iframe.onload = _loadFn; } //记录iframe的加载状态 iframe.state = 0; function _loadFn() { if (iframe.state === 1) { var data = ''; //获取window.name保存的数据 try{ data = iframe.contentWindow.name; }catch(e){ console.log(e); } var json = data; try { json = Kg.JSON.parse(data); } catch(e){} //执行回调方法 _callback(json); //iframe清除 iframe.onload = null; document.body.removeChild(iframe); } else if (iframe.state === 0) { //form提交完成之后,将location置为同域 iframe.removeAttribute('name');//解决IE10+获取不到window.name的问题 iframe.contentWindow.location = "blank.html"; iframe.state = 1; } } function _callback(json) { //默认执行传入的回调方法 if (fn && typeof fn === "function") { fn(json); } else { //没有回调方法则解析postcallback var svalue = url.match(new RegExp("[\?\&]postcallback=([^\&]*)(\&?)")); fn = window[svalue ? svalue[1] : svalue]; if (fn && typeof fn === "function") { fn(json); } } } return false; } http.postJSONP('http://b.csdn.net/cross_domain/json.PHP',{ name: "garygao",password: "******" },function(data){ console.log(data); });
proxy.html就是一个空白的页面:
<!DOCTYPE html> <html> <head> <Meta charset="UTF-8"> <title>空白页面</title> </head> <body> </body> </html>
<?PHP $name = $_POST['name']; if($name) { echo '<script type="text/javascript">'."\n"; echo 'window.name="{succ:true,name:Mike}"'; echo '</script>'."\n"; } ?>
这里只是为了测试,后台返回的数据是json格式的字符串,可以用JSON.parse()或者eval()方式把数据转化成json对象,然后进行数据的操作。
文中提到跨域方案无非就是利用了两种比较经典的解决方式,即隐藏iframe+form和window.name,相对于其他方式,文中的方案算是兼容性比较强的方式了,也无需修改服务器配置和引入额外的Flash文件,唯一不太友好的地方是后端的返回方式比较奇怪,要用<script>包住window.name的赋值,但是这样做也是为了前端更加友好地处理回调数据。
原文链接:https://www.f2er.com/ajax/162071.html