跨域那点事,JSONP的秘密
您目前处于:技术核心竞争力  2017-08-14

如果你是一个WEB开发人员,你肯定遇到过跨域的问题。什么是跨域?我先假装你们不知道(^..^嘻嘻),域名你们都知道,比如www.baidu.com,www.jd.com等都属于域名,如果我想在京东的页面中通过一个关键字来请求百度获取搜索结果,这就是一个跨域请求。

举个例子:民间一位姓陈的人家有一手上好的酿酒本事。他们家立下规矩,这门手艺只传自家孩子。那如果我这个姓何的想去学肯定会被扫地出门,除非得到陈家掌柜的同意才可以。回到我们WEB开发,如果我想在我们京东的页面开发上使用百度的服务,那我如何去做才可以呢?正常来讲浏览器肯定是不允许的,这样会产生严重的安全性问题,具体大家可以Google下CSRF(跨域请求伪造),这里就不具体描述了。

如何跨域这也是一个很久以前让WEB开发人员抓心挠肝的事情。一般的做法是在本地服务器通过构建http请求包来访问目标域名服务器获取服务,而在本地服务器再通过提供一个资源访问地址为WEB客户端提供服务。

这种做法无疑很繁琐,如图所示:

也就是说客户端每次请求京东服务器,京东都要通过网络去访问百度服务器请求服务,这样网络连接不仅多,而且也加重了服务器的负担。

如果能够让客户端直接请求需要跨域才能访问的服务器那当然是特别棒的事情了!如图所示:

这时候JSONP就出现了。说道JSONP,它和JSON有什么关系呢?大家都知道JSON是一种数据描述格式,如xml,protocolbuffer等一样都是组织数据的。JSONP会不会是JSON的一种更加高级的数据描述格式呢?当然不是!JSONP是一种数据调用方式,全名叫JSON with Padding,它的出现是前端工程师利用浏览器的“漏洞”来实现的(前端工程师是有多么苦逼和有才才会发现了这个漏洞)。

其实这个“漏洞”就是所有html中包含有src属性的标签都是可以进行远程请求资源的,比如iframe,img,script,style等等,我们开发WEB页面时都会通过script标签来引入外部的javascript脚本,也会通过img标签来引入外部的图片等资源。浏览器允许这样做来更好的缓存网页中的资源而减少无意义的访问请求。

那当时发明JSONP的那个人就在想(以下纯个人意淫的场景,还请看官勿当真),如果我跨域访问的javascript文件直接执行javascript代码,那是不是就可以解决跨域的问题呢?

1. 于是他顺着这个思路先创建了一个叫crossDomain.js文件放入到需跨域的服务器上。javascript文件的内容如下:

alert(‘我是可以执行的!’);

在本地网页内容里加入:

<script type=”text/javascript”src=”http://www.xxx.com/js/crossDomain.js”></script>

刷新本地页面,会立刻弹出提示框(我是可以执行的!)。

2. 通过这个思路,他又修改了下crossDomain.js文件中的代码。改成如下:

invoke({“name”,”hekun”, age:18});

在本地页面中创建一个函数invoke如下:

functioninvoke(obj){
    alert(“姓名:” + obj.name + “,年龄:” + obj.age);
}

再刷新本地页面,会弹出提示框(姓名:何昆,年龄:18)。这就说明本地函数被跨域的远程javascript调用成功了,并且还接收到javascript文件中的数据。跨域远程获取数据的目的实现了,但是这里还存在两个实际使用的问题:

a. 我如何知道远程javascript调用本地函数的名字是什么呢?

b. 这个数据都是静态的,我怎么才能让它是动态生成的呢?

3. 于是他又做了一件事情,就是在服务端创建了一个action来充当javascript访问的地址,提供了一个动态生成javascript代码的方法代码如下:

public classTestAction extends BaseAction{
    public void crossDomain(){
        String callback =this.getRequest().getParameter(“callback”);
                String id = this.getRequest().getParameter(“id”);
                String json =dataService.getData (id);
                this.getResponse().write(callback + “(” + json + “);”);
    }
}

在本地页面中改造刚才引入script的代码如下:

<script type=”text/javascript”src=”http://www.xxx.com/crossDomain.action?callback=invoke&id=1024”>
</script>

他欣喜的发现调用成功了,同时也返回了预想的结果。讲到这里,其实大家也明白JSONP是如何进行数据调用的了。


总结下JSONP的规则:

a. 客户端页面中在script标签(或javascript动态创建)使用src来请求跨域Action,并提供callback函数名称以及所需数据。

b. 跨域服务器提供一个可以处理JSONP服务请求的Action,并返回客户端页面所需要的javascript代码,格式如:callback(responseJsonData);

c. 客户端页面中创建一个名为callback的函数来处理跨域请求的返回结果;


在日常使用中,大家可以以下几种方式来使用JSONP:

1. 通过script标签加载中执行(不常用)

<script type=”text/javascript”src=”http://www.xxx.com/crossDomain.action?callback=invoke&id=1024”>
</script>

2. 通过javascript脚本动态生成(比较常用,因为可以任何时间执行,而非加载时执行)

//回调函数
function dataHandler(data){
    //处理代码
}
//提供jsonp服务的url地址(什么类型的地址无所谓,只要返回值是一段javascript代码)
var url = “http://www.xxx.com/crossDomain.action?callback=dataHandler&id=1024”;
//创建script标签
var script = document.createElement(“script”);
//传入url
script.setAttribute(“src”, url);
//加入head内并执行
document.getElementsByTagName('head')[0].appendChild(script);

3. 通过jQuery的方式调用(很常用,相比第二种代码要简单很多)

$.ajax({
    type:"get",
    async: false,
    url: " http://www.xxx.com/crossDomain.action?id=1024",
    dataType:"jsonp",
    jsonp:"callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
    jsonpCallback:"dataHandler",//自定义的jsonp回调函数名称,如您不写,可以直接在下面的success中处理数据。
    success:function(json){
        //处理数据
    },
    error: function(){
        //处理异常
    }
});

分享就到这里了,也欢迎大家一块交流。


转载请并标注: “本文转载自 linkedkeeper.com (文/何昆)”  ©著作权归作者所有