jsonp详解

1. 什么是jsonp?

       JSONP(JSON with Padding)是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。

2.JSONP有什么用?

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

3. jsonp是怎么产生的?

       一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准;

       不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>);

       于是,我们可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;

      恰巧,我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据;

      这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。

      客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。

      为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

4. Jsonp原理: 

    1)首先在客户端注册一个callback, 然后把callback的名字传给服务器;

    2)服务器先生成 json 数据;

    3)服务端以 javascript 语法的方式,生成一个function , function 名字就是参数 jsonp中定义的名字;

    4)服务端最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端;

    5)客户端浏览器解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里(动态执行回调函数)。

5. jsonp用法:

 先来看几个示例:

示例1:

客户端jsonp1.html(所有测试文件都放在根目录WEB-INF下): 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript" src="http://172.19.137.52:8080/rm-admin/remote1.js"></script>
</head>
<body>
</body>
</html>
 服务端remote1.js(远程服务端的url:http://172.19.137.52:8080/rm-admin) 
alert("我是远程服务器");
 运行结果: 

     在客户端浏览器中弹出提示框:我是远程服务器。

示例2:

客户端jsonp2.html: 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<head>
    <title></title>
    <script type="text/javascript">
    	var localHandler = function(data){
    		alert("本地函数远程调用remote2.js,获取到的数据是:"+data.result);
    	}
    </script>
    
    <!-- 注意:JavaScript的链接,必须在function的下面。 -->
    <script type="text/javascript" src="http://172.19.137.52:8080/rm-admin/remote2.js"></script>
</head>
<body>
</body>
</html>

 服务端remote2.js 

localHandler({"result" : "我是远程服务器的数据"});

  运行结果: 

      在客户端浏览器中弹出提示框:我是远程服务器的数据。

示例3(jsonp的原生写法):

客户端jsonp3.html: 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<head>
    <title></title>
    <script type="text/javascript">
    	var flightHandler = function(data){
    	   alert("查询到的航班结果是:票价为"+data.price+"元,余票为"+data.tickets+"张。");
    	}

    	// 动态生成调用服务端的js脚本
    	// 远程服务端url
    	var url = "http://172.19.137.52:8080/rm-admin/flight/result.htm?code=CA1401&callback=flightHandler";
    	
	// 创建scrip标签,并设置src属性
    	var script = document.createElement("script");
    	//script.type = "text/javascript";  
    	//script.src = url;
    	// 也可以这么写:
    	script.setAttribute('type', "text/javascript"); 
    	script.setAttribute('src', url); 
    	
    	// 将script标签加入到header
    	document.getElementsByTagName('head')[0].appendChild(script);
    </script>
</head>
<body>
</body>
</html>

 服务端FlightController.java: 

package com.cnsuning.rm.admin.web.controller;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 航班管理 Controller
 * 
 * @author guwq
 */
@Controller
@RequestMapping("/flight")
public class FlightController extends BaseController {

	@RequestMapping(value = "/result", method = RequestMethod.GET)
	@ResponseBody
	public void result(HttpServletRequest request, HttpServletResponse response) {
	    StringBuffer result = new StringBuffer();
	    
	    // 打印获取到的参数
	    String code = request.getParameter("code");
	    String callback = request.getParameter("callback");
	    System.out.println("code==="+code);
	    System.out.println("callback==="+callback);
	    
	    // 拼接jsonp返回结果,在实际情况中,返回结果由后台获取
	    //result = "flightHandler({'price' : '1000', 'tickets' : '50'});";
	    result.append("flightHandler");
	    result.append("({");
	    result.append("\"price\":").append("1000").append(",");
	    result.append("\"tickets\":").append("50");
	    result.append("})");
	    
            // 注意ContentType类型一定要是application/x-javascript
	    response.setContentType("application/x-javascript;charset=UTF-8");
	    PrintWriter out = null;
	    try {
		out = response.getWriter();
		out.println(result);
		out.flush();
		out.close();		
	    } catch (IOException e) {
		logger.error("setResponse IOException" + e.getMessage(), e);
	    } finally {
		if(out != null){
			out.close();
		}
	    }
	}
}
  运行结果: 

       在客户端浏览器中弹出提示框:查询到的航班结果是:票价为1000元,余票为50张。

示例4(jsonp的ajax写法):

客户端jsonp4.html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<head>
    <title></title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.8.2.js"></script>
    <script type="text/javascript">
    	$(function(){
            // 使用ajax来调用jsonp
    	    $.ajax({
                type: "get", //jsonp默认为get请求,即使写post也会转换成get方式
                async: false, // jsonp默认为false,即使写true也会转换成false
                url: "http://172.19.137.52:8080/rm-admin/flight/result.htm", // 服务端地址
                data: {"code" : "CA1405"}, // 入参
                dataType: "jsonp", // jsonp调用固定写法
                jsonp: "callback", // 传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)。即:?callback=xxx中的callback部分
                jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据。即:?callback=xxx中的xxx部分
                success: function(data){ // 调用成功之后的方法
                    alert("查询到的航班结果是:票价为"+data.price+"元,余票为"+data.tickets+"张。");
                },
                error: function(){ // 调用失败之后的方法
                    alert('error');
                }
            });
        });
    </script>
</head>
<body>
</body>
</html>

注解:

注解1:参数jsonp: "callback" 不是必须的,如果该参数为空,则 默认为:jQuery+一个随机字符串,例如:jQuery182025588105828501284_1453863135570,并且执行结果和加上该参数一样。

注解2:参数jsonpCallback:"flightHandler"是必须的,如果该参数为空,则调用失败。

注解3:如果自定义回调函数(即jsonpCallback参数)的函数体不为空,则优先执行该自定义函数,然后执行success中的代码:

 客户端jsonp5.html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<head>
    <title></title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.8.2.js"></script>
    <script type="text/javascript">
    	$(function(){
            // 使用ajax来调用jsonp
    	    $.ajax({
                type: "get", //jsonp默认为get请求,即使写post也会转换成get方式
                async: false, // jsonp默认为false,即使写true也会转换成false
                url: "http://172.19.137.52:8080/rm-admin/flight/result.htm", // 服务端地址
                data: {"code" : "CA1405"}, // 入参
                dataType: "jsonp", // jsonp调用固定写法
                jsonp: "callback", // 传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)。即:?callback=xxx中的callback部分
                jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据。即:?callback=xxx中的xxx部分
                success: function(data){ // 调用成功之后的方法
                    alert("success");
                },
                error: function(){ // 调用失败之后的方法
                    alert('error');
                }
            });
        });

        // 自定义回调函数
        function flightHandler(data){
	   		alert("(普通写法)查询到的航班结果是:票价为"+data.price+"元,余票为"+data.tickets+"张。");
		}
		// 或用原生写法
		var flightHandler = function(data){
			alert("(原生写法)查询到的航班结果是:票价为"+data.price+"元,余票为"+data.tickets+"张。");		
		}
        
    </script>
</head>
<body>
</body>
</html>

 服务端同示例3中的服务端,运行结果也同示例3的运行结果。

相关推荐