JSON跨域原理实现
Ajax不能跨域访问有时候的确有点头疼,但是又不得不这样,如果真的可以跨域了,那就太可怕了。在做PhoneGap开发的时候,将HTML/JavaScript/CSS/Image等文件打包在APK中的时候,如果需要与服务器端数据交互就会产生这个问题,那么有几种方式解决这个数据交互的问题:
1、WebSocket:很强大,不过服务器端要写代码,还要分配socket端口;主要是有些平台内置浏览器不支持WebSocket,且需要写PhoneGap扩展。
2、内嵌Frame:可以在网页中内嵌一个frame,载入服务器端脚本,这样保证在同一个域下请求数据;这种可用性差,需要保证子框架与父框架的数据沟通。
3、JSONP:在JQuery里有Ajax方法中可以设定dataType为JSONP后实现跨域访问。
在web开发中,前端的JavaScript类库,是可以跨域调用的;在上一篇的博客中写过动态载入JavaScript类库:
1 LoadScript : function (url, callback) { 2 var superarg = arguments; 3 var script = document.createElement("script") 4 script.type = "text/javascript"; 5 document.getElementsByTagName("head")[0].appendChild(script); 6 script.onload = function () { 7 if (callback) callback(); 8 }; 9 script.onerror = function(e){ 10 console.log('LoadScript error, url : ' + url); 11 superarg.callee(url, callback); 12 }; 13 script.src = url; 14 },
根据这个原理,服务器端返回的不是JSON数据,而且一个JavaScript脚本,这个脚本中包含了JSON数据,那么在发送之前随机生成一个方法名发送到服务端,服务端返回用这个方法执行所需要的JSON数据,那么在JS被载入到页面中后,便开始执行这个方法,一旦执行这个方法,就可以callback需要执行的函数了,这样就实现了JSON数据跨域请求了,代码如下:
1 getJSON : function (url, callback, complete){ 2 var rNumber = '_'; 3 var reg = /[\?\&]callback=([_\-a-zA-Z0-9]*)/; 4 if(!reg.test(url)) { 5 for (var i = 0; i < 10; i++) { 6 rNumber += Math.floor(Math.random() * 10); 7 }; 8 url.indexOf('?') != -1 9 ? url += '&callback=' + rNumber 10 : url += '?callback=' + rNumber; 11 } 12 else{ 13 var m = reg.exec(url); 14 if(m && m.length > 1){ 15 rNumber = m[1]; 16 } 17 } 18 19 global[rNumber] = function (data) { 20 if (this.fn) this.fn(eval(data)); 21 delete global[this.fnName]; 22 } 23 .bind( { fn : callback, fnName : rNumber } ); 24 25 var script = document.createElement("script") 26 script.type = "text/javascript"; 27 document.getElementsByTagName("head")[0].appendChild(script); 28 script.onload = function () { 29 complete && complete(); 30 $(script).remove(); 31 }; 32 script.onerror = function(e){ 33 console.log('LoadScript error, url : ' + url); 34 }; 35 script.src = url; 36 }
服务器端是asp.net MVC,代码如下:
1 public class JsonpResult : System.Web.Mvc.JsonResult 2 { 3 public override void ExecuteResult(ControllerContext context) 4 { 5 if (context != null) 6 { 7 HttpResponseBase response = context.HttpContext.Response; 8 if (!string.IsNullOrEmpty(ContentType)) 9 { 10 response.ContentType = ContentType; 11 } 12 else 13 { 14 response.ContentType = "application/javascript"; 15 } 16 if (ContentEncoding != null) 17 { 18 response.ContentEncoding = ContentEncoding; 19 } 20 if (Data != null) 21 { 22 HttpRequestBase request = context.HttpContext.Request; 23 JavaScriptSerializer serializer = new JavaScriptSerializer(); 24 response.Write(request.Params["callback"] + "(" + serializer.Serialize(Data) + ")"); 25 } 26 } 27 } 28 }
已将这段代码放入以前实现的JQuery类库中了,地址如下: