Jsonp原理及实现

来源:互联网 发布:淘宝新手如何提高销量 编辑:程序博客网 时间:2024/05/16 09:18

原文有一些错误的地方,本文用红色字体修改成正确的,文章最后也添加了个人对jsonp原理的领悟,

原文地址:http://blog.csdn.net/cqdz_dj/article/details/13629607 

最近做项目,由于前台系统和后台信息发布系统需要部署到不同的服务器上,并且二级域名也不相同,所以经常会遇到一些跨域访问的问题,这些问题基本上可以通过JSONP的方式实现。自己上网找了些JSONP相关的文章来看,做了如下的总结。


1. JSON(JavaScript Object Notation )和JSONP(JSON with Padding)的区别
        JSON和JSONP虽然只有一个字母的差别,但其实他们根本不是一回事儿:JSON是一种数据交换格式,JSONP是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问。JSON对于JavaScript开发人员充满魅力的原因在于JSON本身就是Javascript中的对象。
之所以会有跨域这个问题的产生根本原因是浏览器的同源策略限制。

2. 同源策略限制
        同源策略是指阻止代码获得或者更改从另一个域名下获得的文件或者信息。也就是说我们的请求地址必须和当前网站的地址相同。同源策略通过隔离来自不同源的文件或信息来实现对资源的保护。

3. JSONP的作用
        由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源,为了实现跨域请求,可以通过script标签实现跨域请求,然后在服务端输出JSON数据并执行回调函数,从而解决了跨域的数据请求。

4.JSONP的产生
        解决同源策略限制的一个相对简单的办法就是在服务器端发送请求,服务器充当一个到达第三方资源的代理中继。虽然是用广泛但是这个方法却不够灵活。另一种方式是使用框架要素( iframe)在当前 Web 页面中创建新区域,并且使用 GET 请求获取任何第三方资源。不过,获取资源后,框架中的内容会受到同源策略的限制。
有一个很巧妙的办法就是在页面中使用动态代码元素(script),代码的源指向服务地址并在自己的代码中加载数据。当这些代码加载执行的时候,同源策略就不会起到限制,因为同源策略不阻止动态脚本插入,并且将脚本看作是从提供 Web 页面的域上加载的。客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。

5.JSONP原理: 
1)首先在客户端注册一个callback, 然后把callback的名字传给服务器。
2)服务器先生成 json 数据。
3)然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 jsonp.
4)最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。
5)客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里.(动态执行回调函数)

6.JSONP的具体实现
下面通过一些具体的例子来说明。
1)在本地的JS文件中声明方法,在远程JS中调用,远程JS已经存在。
现在我们在jsonp.html页面定义一个函数,然后在远程remote.js中传入数据进行调用。

        jsonp.html内容如下:

[html] view plaincopyprint?
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"  
  2.         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
  3. <html xmlns="http://www.w3.org/1999/xhtml">  
  4. <head>  
  5.     <title>Jsonp Demo</title>  
  6.     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>  
  7.     <script type="text/javascript">  
  8.         var localHandler = function (data) {//这里的data是远程文件回传的数据  
  9.             alert('本地方法调用跨域的remote.js文件,远程js带来的数据是:' + data.result);  
  10.         };  
  11.     </script>  
  12.     <!--这里的js是远程的文件,文件中包含一个localHandler方法的调用,并传递相应的参数信息-->  
  13.     <script type="text/javascript" src="remote.js"></script>  
  14. </head>  
  15. </html>  

        remote.js的内容如下:

[javascript] view plaincopyprint?
  1. localHandler({"result":"我是远程js带来的数据"});  

        但是,细心观察,就会产生这样的疑问:怎么让远程js知道它应该调用的本地函数叫什么名字呢?毕竟是jsonp的服务者都要面对很多服务对象,而这些服务对象各自的本地函数都不相同啊?这个问题我们留在下一个例子中解决。


2)在本地的JS文件中声明方法,在远程JS中调用,远程JS通过服务端程序动态生成。
聪明的开发者很容易想到,只要服务端提供的js脚本是动态生成的就行了呗,这样调用者可以传一个参数过去告诉服务端“我想要一段调用XXX函数的js代码,请你返回给我”,于是服务器就可以按照客户端的需求来生成js脚本并响应了。
        该实例jsonp.html代码如下:

[html] view plaincopyprint?
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"  
  2.         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
  3. <html xmlns="http://www.w3.org/1999/xhtml">  
  4. <head>  
  5.     <title>Jsonp Demo</title>  
  6.     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>  
  7.     <script type="text/javascript">  
  8.         var localHandler = function (data) {//这里的data是远程文件回传的数据  
  9.             if (data.success) {  
  10.                 window.location.href = data.url;  
  11.             } else {  
  12.                 alert(data.url);  
  13.             }  
  14.         }  
  15.         // 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)  
  16.         var url = "http://demo.jsonp.com/JsonpServlet?callback=localHandler¶m=baidu";  
  17.         // 创建script标签,设置其属性  
  18.         var script = document.createElement('script');  
  19.         script.setAttribute('src', url);  
  20.         // 把script标签加入head,此时调用开始  
  21.         document.getElementsByTagName('head')[0].appendChild(script);  
  22.     </script>  
  23. </head>  
  24. </html>  

        远程调用端通过Servlet向本地写回js方法的调用:

[java] view plaincopyprint?
  1. import javax.servlet.ServletException;  
  2. import javax.servlet.http.HttpServlet;  
  3. import javax.servlet.http.HttpServletRequest;  
  4. import javax.servlet.http.HttpServletResponse;  
  5. import java.io.PrintWriter;  
  6.   
  7. public class JsonpServlet extends HttpServlet {  
  8.     @Override  
  9.     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException {  
  10.         request.setCharacterEncoding("utf-8");  
  11.         response.setCharacterEncoding("utf-8");  
  12.         //获取回调方法名  
  13.         String callback = request.getParameter("callback");  
  14.         //获取参数信息  
  15.         String url = request.getParameter("param");  
  16.         PrintWriter out = response.getWriter();  
  17.         if("baidu".equals(url)){  
  18.             out.write(callback + "({success:true, url:'http://www.baidu.com'})");  
  19.         } else {  
  20.             out.write(callback + "({success:false, url:'http://www.google.com'})");  
  21.         }  
  22.     }  
  23. }  

        web.xml对该Servlet做配置:

[html] view plaincopyprint?
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app xmlns="http://java.sun.com/xml/ns/javaee"  
  3.            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.            xsi:schemaLocation="http://java.sun.com/xml/ns/javaee  
  5.           http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
  6.            version="2.5">  
  7.   
  8.     <servlet>  
  9.         <servlet-name>JsonpServlet</servlet-name>  
  10.         <servlet-class>JsonpServlet</servlet-class>  
  11.     </servlet>  
  12.     <servlet-mapping>  
  13.         <servlet-name>JsonpServlet</servlet-name>  
  14.         <url-pattern>/JsonpServlet</url-pattern>  
  15.     </servlet-mapping>  
  16. </web-app>  

        这次的代码变化较大,不再直接将远程js文件写死,其访问的是远程的服务器端程序,并且JS内容通过远程服务器程序动态生成。以上程序生成的动态的JS文件的内容如下:
[javascript] view plaincopyprint?
  1. localHandler({success:true, url:'http://www.baidu.com'})  

        到现在为止,相信你已经能完全理解JSONP的实现原理了!
        使用JS框架实现JSONP的原理是相同的。jquery框架的实现方式如下:

[html] view plaincopyprint?
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"  
  2.         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
  3. <html xmlns="http://www.w3.org/1999/xhtml">  
  4. <head>  
  5.     <title>Jsonp Demo</title>  
  6.     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>  
  7.     <script type="text/javascript" src="jquery.min.js"></script>  
  8.     <script type="text/javascript">  
  9.         jQuery(document).ready(function () {  
  10.             jQuery.ajax({  
  11.                 asyc: false,  
  12.                 type: "GET",  
  13.                 url: "http://demo.jsonp.com/JsonpServlet?param=baidu",  
  14.                 dataType: "jsonp",  
  15.                 jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为:callback)  
  16.                 jsonpCallback: "localHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名  
  17.                 success: function (data) {  
  18.                     if (data.success) {  
  19.                         window.location.href = data.url;  
  20.                     } else {  
  21.                         alert(data.url);  
  22.                     }  
  23.                 }  
  24.             });  
  25.         });  
  26.     </script>  
  27. </head>  
  28. </html>  

        是不是有点奇怪?为什么有写localHandler这个函数呢?而且竟然也运行成功了!原因是jquery在处理jsonp类型的ajax时自动帮你生成回调函数并把数据取出来供success属性方法来调。

我是学.net的,上面说的是java的实现,下面说说我的理解啊,仅说jquery.ajax对jsonp的实现,

实现原理是这样的,

$.ajax({
            async: false,
            crossDomain:true,
            type: "GET",
            url: "http://www.baidu.com?hello=123&dfasd=dfsfsd",//换成你自己的url和参数
            dataType: "jsonp",
            jsonp: "callback",//可以省略,默认英文诗callback,
            jsonpCallback: "localHandler",//可以省略,默认jquery自动生成一个随机的名字
            success: function(data) {
                //处理逻辑
            },
            error: function(xmlHttpRequest, textStatus, errorThrown) {
                alert(xmlHttpRequest.responseText + "--------" + textStatus + "-------" + errorThrown);
            }
        });

上面是一个模板请求,执行过程是这样的:

你发送一个ajax请求,jquery根据你指定的jsonpCallback参数在本页面中先生成一个你指定名字的函数(若你没有自己指定,要自己指定,必须是window下的函数),然后在生成一个script标签,src指向你指定的url发送ajax请求,发送前,会再你的url后面追加jsonp和jsonpCallback指定的参数,上面例子会追加如下:&callback=localHandler,然后读取你server端的返回,你的返回必须是可执行的js代码,上面的例子为例,也就是localHandler(jsonobj),这种形式,接着jquer要调用localHandler函数,接着调用success函数,并把jsonobj传给success函数,则此跨域调用就完成了


0 0
原创粉丝点击