JSON(Ajax)和JsonP
当我们开发一个WEB应用时,不可避免地使用一些Ajax来实现异步数据加载,以构建富客户端。
不过使用Ajax有一个问题:同源策略可能会使我们无法取得所需数据。
(注:同源策略指Ajax的请求方和数据返回者必须在同一IP和端口下,例如我们的网页地址在127.0.0.1:8080下,那么它发起的Ajax请求就只能被此IP和端口的监听程序接收和处理。有网友指出即时同一台主机的多个IP也不可以互发Ajax请求,这个我没有求证。)
由于Ajax的同源策略,造成我们在以下情况下可能无法取得所需数据(就Ajax而言):
1.需要在页面上使用脚本(JavaScript)去取得其他服务器的数据(不同于WebService,我们一般在后台使用Http请求来取得数据然后返回给浏览器)。
2.需要将网页保存到本地(即脱机使用),在浏览器加载网页时使用脚本从服务器获取数据。
特别是第二种,在现今移动应用开发非常适用。由于各个移动设备平台都提供了很好支持,使用Html5和Css3标准的网页甚至可以媲美本地应用,而且其跨平台特性比至于本地应用更具优势!
那么说了Ajax在“跨域”上的局限性之后我们不得不使用一些技巧来实现技能异步加载和跨域。
JsonP映入我们的眼帘,JsonP是一种技巧或使用模式。它也使用了Json格式传输数据,但这并非必要(Ajax也如此)。
我们使用Ajax不能跨域时又发现:
1.普通的Http请求可以跨域。
2.<script src=""></script>、<link ref=""/>、<img src=""/>这些标签数据加载时使用的是Http请求,而且不是同步的(此处笔者没有过多研究,如果有兴趣,读者可以试试阅读WebKit浏览器内核源码)!!!
那么我们是否可以使用脚本标签来实现跨域数据请求呢?答案是肯定的!
我们知道:只要是数据请求,一定是有一定规则的。实质上网页、图片、css和js都是使用Http请求(一般为Get)来实现的,服务器程序会返回对应数据(如js脚本内容、css样式表内容、图片内容等),只不过我们平时在进行服务器程序逻辑设计时将资源文件请求忽略了,让服务器程序来默认处理。
至此,JsonP跨域原理就已经很清晰了:使用<script></script>标签来产生Http(Get)请求来从服务器获取数据(平时我们是请求具体的资源文件如jquery.js,而服务器程序会默认把对应文件以字符串形式返回给浏览器来处理;而现在我们需要请求具体的控制器,让服务器程序返回的字符串里包含我们所需要的数据)。
为了便于读者理解,我绘制了一副草图:
从上图不难看出:我们拜托了JavaScript帮我们带回数据。
以下是具体代码:读者如果不是很理解的话可以从CallBack作为突破口,理解了它,JsonP基本就很清晰地呈现在你面前了。
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 2 <html> 3 <head> 4 <title> New Document </title> 5 <meta name="Generator" content="EditPlus"> 6 <meta name="Author" content=""> 7 <meta name="Keywords" content=""> 8 <meta name="Description" content=""> 9 <script type="text/javascript"> 10 function loadJsonP(){ 11 var url = "http://localhost:8080/test/jsonp.do?callback=CallBack"; 12 var jsonpScript = document.createElement('script'); // 创建为我们服务的脚本标签 13 jsonpScript.setAttribute('src', url); // 设置src 14 document.getElementsByTagName('head')[0].appendChild(jsonpScript); // 将标签添加到页面,浏览器会以标签src属性为url获取脚本内容 15 } 16 // 脚本内容即为对下面函数的调用,同时传回了数据 17 function CallBack(data){ 18 // 对数据的处理... 19 var showarea = document.getElementById('ta'); 20 showarea.innerHTML = "数据取回!id:" + data.id + "name:" + data.name; 21 } 22 </script> 23 </head> 24 25 <body> 26 <textarea id="ta"></textarea> 27 <a href="javascript:loadJsonP()">jsonP</a> 28 </body> 29 </html>
(上面的代码为本地html网页,即并非通过浏览器地址栏输入来访问服务器网页)
1 public void doGet(HttpServletRequest request, HttpServletResponse response) 2 throws ServletException, IOException { 3 response.setCharacterEncoding("gbk"); 4 response.setContentType("text/javascript"); 5 PrintWriter out = response.getWriter(); 6 String json = "{\"id\": \"100\",\"name\": \"张三\"}"; 7 String callback = request.getParameter("callback"); 8 /*try { 9 Thread.sleep(3000); 10 } catch (InterruptedException e) { 11 e.printStackTrace(); 12 }*/ 13 out.write(callback+"("+json+")"); 14 out.flush(); 15 out.close(); 16 }
(上面的代码可以用于返回脚本字符串,同时带回数据。只要客户端和服务端定义好回调函数名即可)
(最后编辑时间2013-06-13 22:44:57)