最近在做微信公众号的微网页,有两个方案,一是使用原来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,还有一些参数,然后直接写个回调函数,就可以拉。
就想到了这么多,还有多个接口调用的,具体就不写了。。。。。。下班