跨域调用资源的方法之——JSONP的原理实例

posted @ Sep 22, 2015

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>

//添加<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:

        
function result(data) {
//我们就简单的获取数据,然后对数据进行有效的处理
    console.log(data);
}
  • callback=?这个是正如其名表示回调函数的名称,也就是将你自己在客户端定义的回调函数的函数名传送给服务端,服务端则会返回以你定义的回调函数名的方法,将获取的json数据传入这个方法完成回调。

名词解释

百度百科 同源策略,它是由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实现跨域的原理。


具体操作

原理我们知道了,该怎么实现这些操作呢?上面我使用了原生方法的实现原理 接下来轮到jQuery登场!JQ已经帮我们封装好了 demo:

$.ajax({
    dataType:'jsonp',
    jsonp:'jsonp_callback',
    url:'http://www.baidu.com/xxx.jsonp',
    success:function(){
        //dosomthing
    }
});

以jQuery2.1.3的ajax方法为例

$.ajax({
    url:"",
    dataType:"jsonp",
    data:{
        params:""
        }
}).done(function(data){
    //dosomething..
})
  • 仅仅是客户端使用jsonp请求数据是不行的,因为jsonp的请求是放在script标签中的,和普通请求不同的地方在于,它请求到的是一段js代码,如果服务端返回了json字符串,那么浏览器就会报错。所以jsonp返回数据需要服务端做一些处理。比如找PH语言中处理传过去的数据,再议回调函数的形式返回。

服务端返回数据处理

1.上面说了jsonp的原理是利用script标签来解决跨域,但是script标签是用来获取js代码的,那么我们怎么获取到请求的数据呢。

2.这就需要服务端做一些判断,当参数中带有callback属性时,返回的type要为application/javascript,把数据作为callback的参数执行。下面是jsonp返回的数据的格式示例

typeof jQuery21307270454438403249_1428044213638 === 'function' && jQuery21307270454438403249_1428044213638({"code":1,"msg":"success","data":{"test":"test"}});

这是express4.12.3关于jsonp的实现代码

// jsonp
if (typeof callback === 'string' && callback.length !== 0) {
this.charset = 'utf-8';
this.set('X-Content-Type-Options', 'nosniff');
this.set('Content-Type', 'text/javascript');
// restrict callback charset
callback = callback.replace(/[^\[\]\w$.]/g, '');
// replace chars not allowed in JavaScript that are in JSON
body = body
  .replace(/\u2028/g, '\\u2028')
  .replace(/\u2029/g, '\\u2029');
// the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"
// the typeof check is just to reduce client error noise
body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');';
}