JSONP即JSON with Padding。由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源。如果要进行跨域请求,我们可以通过使用 html的script标记来进行跨域请求,并在响应中返回要执行的script代码,其中可以直接使用JSON传递javascript对象。这种跨域 的通讯方式称为JSONP。
很明显,JSONP是一种脚本注入(Script Injection)行为,需要特别注意其安全性。
在asp.net中实现简单的JSONP非常简单。我们需要两个页面,分别承担协议的客户端和服务器端角色。
假设我们有两个站点,http://www.baa.com.cn 和 http://www.bitauto.com
我们的客户端页面为 http://www.baa.com.cn/JSONPClient.aspx ;而我们的服务器端页面为 http://www.bitauto.com/JSONPServer.aspx 。
首先,我们来做客户端页面:
==========Javascript代码段1:JSONP客户端发送请求的方法=================
function CallJSONPServer(url){ // 调用JSONP服务器,url为请求服务器地址
var oldScript =document.getElementById(url); // 如果页面中注册了调用的服务器,则重新调用
if(oldScript){
oldScript.setAttribute("src",url);
return;
}
var script =document.createElement("script"); // 如果未注册该服务器,则注册并请求之
script.setAttribute("type", "text/javascript");
script.setAttribute("src",url);
script.setAttribute("id", url);
document.appendChild(script);
}
------------------END------------------------
===========Javascript代码段2:JSONP客户端对服务器开放的接口方法=======================
function OnJSONPServerResponse(obj){
alert(obj);
}
------------------END------------------------
===========HTML代码段1:JSONP客户端向用户提供的UI===============
<input type="button" onclick="CallJSONPServer('http://www.bitauto.com/JSONPServer.aspx')" />
------------------END------------------------
至此,JSONP客户端完成,接下来我们做JSONP服务器端页面:
============C#代码段1:应答JSONP客户端请求================
protected void Page_Load(object sender, EventArgs e)
{
Response.DisableKernelCache();
Response.Cache.SetNoStore();
Response.Write("OnJSONPServerResponse('" + DateTime.Now.ToString() + "');");
Response.End();
}
=======================END=================================
jsonP目前被用来作为跨域的首选方案.即用动态创建的script标签去回调后端脚本,然后获取返回值并调用回调函数。但这里面有两个比较遗憾的事情:
- 前端需要和后端提前定义好接口,而且定义接口后,如果前端回调函数要改变名字,那么后端也需要变。(比如串行化,一个接口调用不同的回调函数,那么就更麻烦了)。
- 无法捕获错误信息。
为了处理这两点。从好奇和emu那边看到了一个比较好的解决方案。转载记录一下
var ua = navigator.userAgent;
if ( / MSIE / .test(ua)) {
return function (param) {
charset = param.charset || ' gb2312 '
var frag = document.createDocumentFragment(), script = frag.createElement( ' script ' );
script.charset = charset;
frag[param.name] = function () {
param.callback && param.callback.apply( null , arguments);
frag = script = script.onreadystatechange = frag[param.name] = null ;
};
script.onreadystatechange = function () {
if (script.readyState == ' loaded ' ) {
param.errorcallback && param.errorcallback();
frag = script = script.onreadystatechange = frag[param.name] = null ;
}
};
script.src = param.url;
frag.appendChild(script);
};
} else {
return function (param) {
charset = param.charset || ' gb2312 '
var iframe = document.createElement( ' iframe ' );
iframe.style.display = ' none ' ;
iframe.callback = function () {
param.callback && param.callback.apply( null , arguments);
iframe.callback = iframe.errorcallback = null ;
iframe.src = ' about:blank ' , iframe.parentNode.removeChild(iframe), iframe = null ;
};
iframe.errorcallback = function () {
param.errorcallback && param.errorcallback();
iframe.callback = iframe.errorcallback = null ;
iframe.src = ' about:blank ' , iframe.parentNode.removeChild(iframe), iframe = null ;
};
try {
if ( / KHTML / .test(ua)) iframe.src = ' block.htm ' ; // 404可以
iframe.onload = function () {
iframe.onload = null ;
// iframe.src="javascript:\"<scrip"+"t>function " + param.name + "(){frameElement.callback.apply(null, arguments)};<\/scrip"+"t><scrip"+"t src='"+param.url+"'><\/scrip"+"t><scrip"+"t>setTimeout('frameElement.errorcallback()',0)<\/scrip"+"t>\"";
iframe.contentWindow.document.open();
iframe.contentWindow.document.write(
' <script type="text\/javascript">function ' + param.name + ' () { frameElement.callback.apply(null, arguments); }<\/script> '
+ ' <script type="text\/javascript" src=" ' + param.url + ' " charset=" ' + charset + ' "><\/script> '
+ ' <script type="text\/javascript">window.setTimeout("try { frameElement.errorcallback(); } catch (exp) {}", 1)<\/script> '
);
iframe.contentWindow.document.close();
};
document.body.insertBefore(iframe, document.body.firstChild);
} catch (exp) {}
};
}
}();
【使用方法】
name: ' callback ' , // 和后端默认的接口
url: ' ajax.php ' , // 请求地址
callback: result, // 自定义的回调函数(这样就和后端无耦合了)
errorcallback: error // 错误回调函数
});