微网页关于跨域的一些相关问题
最近在做微信公众号的微网页,有两个方案,一是使用原来web端的内容,直接改页面,改成手机端的样子,然后把一些多余的东西去掉,这样的话页面也需要改动,后台也需要改动。
二是直接用app的接口,因为页面设计的其实和app差不多,用到的数据基本都一样,这样的话可以不用写后台的接口了,只需要做页面,然后调接口,显示数据就可以了。
然而遇到了第一个问题就是,普通的ajax请求无法使用,原因是跨域了。具体跨域就不多说了,一般跨域都是使用jsonp来调接口。
然而。。。jsonp只能使用GET方式,而且我们的app的接口传回的数据是压缩的json。不是直接可以用的json,无法解压。
后来。。。考虑到正好页面是用H5做的,就用H5的跨域来解决吧~
H5的跨域说起来很简单,无非就是:
1.页面和其打开的新窗口的数据传递
2.多窗口之间消息传递
3.页面与嵌套的iframe消息传递
4.上面三个问题的跨域数据传递
postMessage()
这些问题都有一些解决办法,但html5引入的message的API可以更方便、有效、安全的解决这些难题。postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
postMessage(data,origin)方法接受两个参数
1.data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。
2.origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
具体我们是如何操作的呢?
首先页面上需要引入iframe:
<iframe id="child" src="http://m.nbcyl.com/resources/app/resultData.html" width="0" height="0"></iframe>
这个html里面有什么呢?
<!doctype html> <html> <head> </head> <body > <script type="text/javascript" src="http://m.nbcyl.com/resources/admin/js/jquery.js"></script> <script type="text/javascript"> function getData(data) { var array = new Array(); var json = JSON.parse(data); array[0]=json.url; var oReq = new XMLHttpRequest(); var params =''; if (json.type == 'GET') { params = '?' + json.params; } else if (json.type == 'POST'){ params = json.params; } oReq.open(json.type, json.url + params, true); oReq.responseType = "arraybuffer"; oReq.onreadystatechange=function() { if (oReq.readyState==4 && oReq.status==200) { var arrayBuffer = oReq.response; // Note: not oReq.responseText if (arrayBuffer) { var byteArray = new Uint8Array(arrayBuffer); array[1]=arrayBuffer; window.parent.postMessage(array,'*'); } } } if (json.type == 'GET') { params = null; } oReq.send(params); } //getData(); var container=document.getElementById('container'); window.addEventListener('message',function(e){ var data = e.data; if(e.source!=window.parent) return; getData(data); },false); </script> </body> </html>
简单说一下过程,其实就是在页面上发送postMessage方法,到iframe页面里,这个页面是在那个跨域的URL服务器里的,然后 到了这个HTML里,有一个监听方法,来获取发送的数据,然后在此发送给具体的接口,并返回数据。
我们是提取了一个公共的js:
var waterfall = { pageSize:3, pageNumber:1, ajaxType: "GET", url: "", urls:new Array(), /** * 多个调用跨域链接 根据url * @param url * 后台接口地址 * @param arrayFilters * 接口参数params */ setWaterFalls:function (url,arrayFilters,success) { //var url = waterfall.urls; var type = waterfall.type; var template = waterfall.template; var pageNumber = waterfall.pageNumber; // 拼接参数(旧格式key:value,key:value) //var params ={pageNumber: pageNumber, pageSize: waterfall.pageSize}; // if (arrayFilters != null && arrayFilters != "") { // var filters = arrayFilters.split("&"); // for (var x = 0; x < filters.length; x++) { // var filter = filters[x].split("="); // params[filter[0]]=filter[1]; // } // } // 拼接参数(新格式key=value&key=value) var params = 'pageNumber='+pageNumber+'&pageSize='+waterfall.pageSize; params+='&'+arrayFilters; waterfall.getWaterFalls(url,type,params,success); }, /** * 调多个接口 发送链接 跨域 * @param url * 后台地址 * @param type * 请求类型 * @param params * 参数条件 json:{pageNumber=1, pageSize=pageSize, Id=100} * @param success * 成功回调函数 */ getWaterFalls : function (url,type,params,success) { // $.ajax({ // url:url, // type: type, // data: params, // jsonpCallback:"success_jsonpCallback",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名 // dataType: "jsonp", // success:function(data){ // success(data); // } // }); var message = JSON.stringify({url:url,type:type,params:params}); //发送跨域页面传递消息 页面在发送请求 window.frames[0].postMessage(message,'http://m.nbcyl.com'); //监听回调 解压数据 window.addEventListener('message',function(e){ if (e.returnValue) { e.returnValue=false; var url = e.data[0]; str = pako.ungzip( e.data[1], { to: 'string' } ); var result = JSON.parse(str); success(url,result); } },false); }, /** * 单个调用跨域链接 根据url * @param url * 后台接口地址 * @param arrayFilters * 接口参数params */ setWaterFall:function (arrayFilters,success) { var url = waterfall.url; var type = waterfall.type; var template = waterfall.template; var pageNumber = waterfall.pageNumber; // 拼接参数(新格式key=value&key=value) var params = 'pageNumber='+pageNumber+'&pageSize='+waterfall.pageSize; params+='&'+arrayFilters; waterfall.getWaterFall(url,type,params,success); }, /** * 调单个接口 发送链接 跨域 * @param url * 后台地址 * @param type * 类型 * @param params * 参数条件 json:{pageNumber=1, pageSize=pageSize, categoryId=100} * @param success * 成功回调函数 */ getWaterFall : function (url,type,params,success) { var message = JSON.stringify({url:url,type:type,params:params}); window.frames[0].postMessage(message,'http://m.nbcyl.com'); //发送请求 并解压数据 window.addEventListener('message',function(e){ if (e.returnValue) { e.returnValue=false; str = pako.ungzip( e.data[1], { to: 'string' } ); var result = JSON.parse(str); success(result); } },false); }, };
具体在页面上使用就可以直接这样:
//列表参数 var params = "orderType=dateDesc"; window.onload=function(){ //最新推荐 waterfall.url="http://m.nbcyl.com/app/integration/search.jhtml"; waterfall.type="GET"; waterfall.pageNumber=1; waterfall.pageSize=8; waterfall.setWaterFall(params,success); //回调函数 function success(message) { } }
设置一下url,type,还有一些参数,然后直接写个回调函数,就可以拉。
就想到了这么多,还有多个接口调用的,具体就不写了。。。。。。下班