网上找了些关于Jsonp的文章看,写的都有点混乱。在这里好好整合下。
一、名词解释
百度百科同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支JavaScript的浏览器都会使用这个策略。 所谓同源是指,域名,协议,端口相同。当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面当浏览器的百度tab页执行一个脚本的时候会检查这脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。
-
同源策略限制了我们无法通过原生的XMLHttpRequest()对象获取到json数据。为了突破这个限制,一个有效简单的解决方案就是:jsonp。目前主流网站首选的是jsonp来跨域请求。
-
jsonp并非新的数据格式,而是解决JSON跨域获取的解决方案。通过JSONP获取到得数据已经不是JSON了,而是JS类型的数据(大部分是对象)。给的是一个回调函数,函数里是请求的数据组成的一层层对象。
-
上网找过很多讲jsonp的文章,大部分都是讲的模模糊糊的。jsonp的原理其实不复杂: 1、浏览器的同源策略把跨域请求都禁止了; 2、HTML的
<script>
标签是例外,可以突破同源策略从其他来源获取数据;HTML里还有一个图像Ping技术,就是图像加载也是可以跨域的,这个主要是用于广告跟踪等。 3、由上可得,我们可以通过<script>
标签引入jsonp文件,然后通过一系列JS操作获取数据。上面三点便是JSONP实现跨域的原理。
二、JSONP的原理概述
javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。
-
Jsonp个人的理解是后端向前端传输JSON格式数据一种方式,形如
callback({"name":"Joy","age":"22","gender":"male"})。
callback("a")
这种虽然包裹的数据不是JSON格式的,但应该也算吧? -
实现JSONP的
javascript callback
的形式,本地脚本预先定义一个callback(data)
函数,然后向支持jsonp的服务发起一个请求,服务器一般会返回callback(data)
的形式,这样就变相获得并操纵数据。 -
一般形式
<scripts src="http://www.*.com?callback=callback"></script>
-
callback=?这个是正如其名表示回调函数的名称,也就是将你自己在客户端定义的回调函数的函数名传送给服务端,服务端则会返回以你定义的回调函数名的方法,将获取的json数据传入这个方法完成回调。
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}
window.onload = function() {
//将自定义的回调函数名result传入callback参数中
addScriptTag("localhost/bns-relation/index.PHP?r=BnsRelation/BnsOfProd&callback=result");
} 回调函数的写法,自定义的回调函数result:
result(data) {
//我们就简单的获取数据,然后对数据进行有效的处理
console.log(data);
}
三、实现原理步骤
原理我们知道了,该怎么实现这些操作呢?上面我使用了原生方法的实现原理 接下来轮到jQuery登场!JQ已经帮我们封装好了 demo:
$.ajax({
dataType:'jsonp',jsonp:'jsonp_callback',url:'http://www.baidu.com/xxx.jsonp',success:(){
//dosomthing
}
});
以jQuery2.1.3的ajax方法为例
$.ajax({
url:"",dataType:"jsonp",data:{
params:""
}
}).done((data){
//dosomething..
})
- 仅仅是客户端使用jsonp请求数据是不行的,因为jsonp的请求是放在script标签中的,和普通请求不同的地方在于,它请求到的是一段js代码,如果服务端返回了json字符串,那么浏览器就会报错。所以jsonp返回数据需要服务端做一些处理。比如找PH语言中处理传过去的数据,再议回调函数的形式返回。
服务端返回数据处理
现在前端写好了,还需要对服务器端程序做一些修改才可正确接收内容。由上面解释可知前端希望服务器端Jsop返回的应该是一段JS代码(JS函数),我们就构造一个JS函数 jsonpCallback(你在前端页面定义的函数)。现在可正确拿到服务端数据
@RequestMapping("rankTest") @ResponseBody public String rank() { Map map = new HashMap(); map.put("a",4); String str = JSON.toJSONString((map)); return "jsonpCallback(" + str + ")"; }Step 1 : 添加JSONP转换器 public class JsonpHttpMessageConverter extends MappingJackson2HttpMessageConverter { @Override protected void writeInternal(Object object,HttpOutputMessage outputMessage) throws IOException,HttpMessageNotWritableException { JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType()); JsonGenerator jsonGenerator = this.getObjectMapper().getFactory().createJsonGenerator(outputMessage.getBody(),encoding); try { //ConfigContainer.JSONP_CALLBACK 为回调名称,如"callback" jsonGenerator.writeRaw(ConfigContainer.JSONP_CALLBACK); jsonGenerator.writeRaw('('); this.getObjectMapper().writeValue(jsonGenerator,object); jsonGenerator.writeRaw(");"); jsonGenerator.flush(); } catch (JsonProcessingException ex) { throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(),ex); } } } step 2 : 添加配置 <mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="...JsonpHttpMessageConverter" p:supportedMediaTypes="application/jsonp"/> </mvc:message-converters> </mvc:annotation-driven> 前端页面测试: $.ajax({ type: <your type>,url: <your url>,dataType: 'jsonp',jsonpCallback: 'JsonpCallback',//这个值要与第一步的ConfigContainer.JSONP_CALLBACK同名 contentType: 'application/jsonp;charset=UTF-8',}).done(function (result) { //TODO }).fail(function (result,textStatus,info) { //TODO }); }