前端跨域实践(二):JSONP

上一篇文章 前端跨域(一):CORS 实现了跨域的一种解决方案,IE8 和其他浏览器分别通过 XDomainRequest 和 XHR 对象原生支持 CORS。这次我将补一补 Web 服务中也非常流行的一种跨域技术——JSONP,同时,将复用上次的前端跨域场景。

1. JSONP(JavaScript Object Notation with padding,填充式JSON/参数式JSON)

【简单理解】:JSONP = 回调函数(Padding) + 数据(JSON),可以将 Padding 理解为回调函数,JSONP 则为被包含在函数调用中的 JSON。

callback({ "name": "Nicholas" });

【原理】:Ajax 的跨域受到“同源策略”的限制,但是像 <script><img> 标签带有 src 属性,都可不受限制地从其他域中加载资源,JSONP 则是通过动态 <script> 元素来使用的。

【实现方式】:回调函数是当响应到来时应该在页面中调用的函数,其名字一般在请求中指定。在请求完成后,即 JSONP 响应加载到页面中以后,就会立即执行。

function handleResponse(response) {
    alert(response.city);
}

var script = document.createElement('script');
// 指定回调函数的名字为 handleResponse
script.src = 'http://freegeoip.net/json/?callback=handleResponse';
document.body.insertBefore(script, document.body.firstChild);

【优点】:能够直接访问响应文本,支持在浏览器域服务器之间双向通信。
【缺点】:JSONP 从其他域中加载代码执行,存在安全隐患,因此,使用时需要保障web服务安全可靠。

2. 本地模拟 JSONP 跨域

接下来让我们来一步一步实现 JSONP,从中我们也将看到,JSONP 跟 AJAX 毫无关系。

(1)首先,如果不使用 JSONP,实现一个正常的函数调用:创建 2 个 <script> 标签,一个用来定义函数以便处理数据,另一个则用来进行函数调用。

通过<script>进行函数调用

(2)接着,我们将第二个 <script> 标签中的函数调用放到单独的 js 文件中,更改一下传入参数,进行验证。

通过引入 js 文件进行函数调用

(3)到此为止, callback 函数是在本地的 js 文件中被调用的。现在,假设这个 js 文件在服务端,需要通过请求才能获得,则给 <script> 标签指定相应的 src 即可。

Server 端:

var http = require('http');

http.createServer(function(req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    // 发送字符串,等客户端获得响应时,即会调用该函数
    res.end("callback('This is the callback from server.')");
}).listen(8888);
通过请求服务调用回调函数
服务器写死的回调函数名称

(4)上面的回调函数是在服务端写死的,而现实的情况,应该是客户端以参数的形式将回调函数名称传递给服务端,服务端获取这个变量,从而进行调用。这样,服务端就不用关心这个回调函数的名称是否改变了,而且前端也可以自行定义回调函数的名称。

OK,既然要传参,就得约定参数 key 值,这也是JSONP中唯一需要前后端一起约定字段的地方

通过在请求URL中加上查询参数实现JSONP

Server:

var http = require('http');
var url = require('url');

http.createServer(function(req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});

    // 解析 url 参数
    var params = url.parse(req.url, true).query;
    // jsonpCallback 为前后端约定的字段,用于获取回调函数的名称
    res.end(params.jsonpCallback + "('This is JSONP.')");
}).listen(8888);
回调函数通过查询参数获得

(5)最后一步:为了提高代码的灵活性,实现 <script> 标签动态插入

<script>
    // 定义回调函数
    var cb = function(data) {
        var oDiv = document.getElementById('content');
        oDiv.innerHTML = data;
    }

    var url = 'http://localhost:8888?jsonpCallback=cb';

    // 创建 script 标签,并设置其 src 属性
    var script = document.createElement('script');
    script.src = url;

    // 插入 body,此时调用开始
    document.body.appendChild(script);
</script>

瞧,整个过程,我们并没有用到 XHR 对象,只是利用了 <script>src 属性,因此 JSONP 与 AJAX 并不是一回事儿。

3. jQuery 实现 JSONP

jQuery 是前端经常使用的库,因此有必要了解 JSONP 在 jQuery 中的使用方式。
jQuery 将其包含在 $.ajax() 中,其中用到的具体有3个参数:

dataType(默认:none):预期服务器返回的数据类型('json', 'jsonp', 'xml', 'html', 'text')
jsonp(默认:“callback”): JSONP回调查询参数的名称,即前后端约定的字段名
jsonpCallback(默认:“jsonp{N}”):全局JSONP回调函数的字符串(或返回的一个函数)名。设置该项能启用浏览器的缓存。

Client:

var oDiv = document.getElementById('content');

// 定义回调函数
// 只是用于服务端获取名称,也可以自行实现,从而在 `success` 中进行调用
var cb = function() {};

$.ajax({
    url: 'http://localhost:8888',
    type: 'get',
    dataType: 'jsonp',  // 预期服务器返回的数据类型
    jsonp: 'A_callback',  // 指定回调查询参数的名称,即前后端约定的字段,默认为“callback"
    jsonpCallback: 'cb',  // 指定回调函数名称
    cache: true,
    success: function(data) {   // jQuery 将 JSON 数据剥离出来,传入 success 和 error
        console.log(data);  // 'This is JSONP realized by jQuery.'
        oDiv.innerHTML = data;
    }
});

Server:不变

var http = require('http');
var url = require('url');

http.createServer(function(req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});

    // 解析 url 参数
    var params = url.parse(req.url, true).query;
    // A_callback 为前后端约定的字段,用于获取回调函数的名称
    res.end(params.A_callback+ "('This is JSONP realized by jQuery.')");
}).listen(8888);
jQuery实现JSONP

jsonp 可省略,或设置为 false,则查询参数就不会出现在 URL 中了,但是回调函数的名称需要前后端约定,因为无法从请求中获取回调函数的名称,后端只能将名称写死。

jsonpCallback 也可省略,jQuery 会自动生成一个随机字符串作为函数名,可以减少不必要的命名工作。

4. 参考

5分钟彻底明白 JSONP
Understanding JSONP

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,183评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,850评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,766评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,854评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,871评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,457评论 1 311
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,999评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,914评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,465评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,543评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,675评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,354评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,029评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,514评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,616评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,091评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,685评论 2 360

推荐阅读更多精彩内容