JsonP跨域问题研究

前端之家收集整理的这篇文章主要介绍了JsonP跨域问题研究前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

网上找了些关于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数据传入这个方法完成回调。

//添加<script>标签方法

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(你在前端页面定义的函数)。现在可正确拿到服务端数据

 
 
四、每个函数都这样写太不方便,不便于接口的操作。还好Spring为我们提供了方便,修改默认的转换器MappingJackson2HttpMessageConverter即可。
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
    });
}
@RequestMapping("rankTest") @ResponseBody public String rank() { Map map = new HashMap(); map.put("a",4); String str = JSON.toJSONString((map)); return "jsonpCallback(" + str + ")"; }
提示:在本地启动两个服务器,端口不一样,可模拟跨域访问。

猜你在找的Json相关文章