jQuery源码分析系列(35) : Ajax - jsonp的实现与原理
ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本
json核心就是:允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
jquery ext dojo这类库的实现手段其实大同小异
在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,但img、iframe、script等标签是个例外,这些标签可以通过src属性请求到其他服务器上的数据。
利用script标签的开放策略,我们可以实现跨域请求数据,当然,也需要服务端的配合。
先看一段jQuery处理jsonp的情况
通过发送php请求
客户端
$.ajax({
async: false, // 同步加载数据,即等到ajax执行完毕再接着执行下面的语句
url: 'http://192.168.1.114/yii/demos/test.php', //不同的域
type: 'GET', // jsonp模式只有GET是合法的
data: {
'action': 'aaron'
}, // 预传参的数组
dataType: 'jsonp', // 数据类型
jsonp: 'backfunc', // 指定回调函数名,与服务器端接收的一致,并回传回来
success: function(json) {
console.log(json);
}
})
php服务端
<?php
$act = trim($_GET['action']); if($act == 'aaron' ){
echo trim($_GET['backfunc']).'('. json_encode(array('status'=>1,'info'=>'OK')) .')';
}
?>
一般的ajax是不能跨域请求的,因此需要使用一种特别的方式来实现跨域,其中的原理是利用 <script> 元素的这个开放策略
这里有2个重要的参数
- jsonpCallback:
为jsonp请求指定一个回调函数名。这个值将用来取代jQuery自动生成的随机函数名。这主要用来让jQuery生成一个独特的函数名,这样管理请求更容易,也能方便地提供回调函数和错误处理。你也可以在想让浏览器缓存GET请求的时候,指定这个回调函数名。从jQuery 1.5开始,你也可以使用一个函数作为该参数设置,在这种情况下,该函数的返回值就是jsonpCallback的结果。
- jsonp:
在一个jsonp请求中重写回调函数的名字。这个值用来替代在"callback=?"这种GET或POST请求中URL参数里的"callback"部分,比如{jsonp:'onJsonPLoad'}会导致将"onJsonPLoad=?"传给服务器。在jQuery 1.5,,设置jsonp选项为false,阻止了jQuery从加入"?callback"字符串的URL或试图使用"=?"转换。在这种情况下,你也应该明确设置jsonpCallback设置。例如, { jsonp: false, jsonpCallback: "callbackName" }、
当我们正常地请求一个JSON数据的时候,服务端返回的是一串JSON类型的数据,而我们使用JSONP模式来请求数据的时候
服务端返回的是一段可执行的JavaScript代码
所以我们可见服务器代码最后一行
$_GET['backfunc']).'('. json_encode(array('status'=>1,'info'=>'OK')) .')
就是执行的 backfunc方法,然后把数据通过回调的方式传递过去
OK,就是整个流程就是:
客户端发送一个请求,规定一个可执行的函数名(这里就是jQuery做了封装的处理,自动帮你生成回调函数并把数据取出来供success属性方法来调用,不是传递的一个回调句柄),服务端接受了这个backfunc函数名,然后把数据通过实参的形式发送出去
jQuery的实现:
通过ajax请求不同域的实现,底层不是靠XmlHttpRequest而是script,所以不要被这个方法给迷惑了
在ajax请求中类型如果是type是get post,其实内部都只会用get,因为其跨域的原理就是用的动态加载script的src,所以我们只能把参数通过url的方式传递
比如
$.ajax({
url: 'http://192.168.1.114/yii/demos/test.php', //不同的域
type: 'GET', // jsonp模式只有GET是合法的
data: {
'action': 'aaron'
}, // 预传参的数组
dataType: 'jsonp', // 数据类型
jsonp: 'backfunc', // 指定回调函数名,与服务器端接收的一致,并回传回来
})
其实jquery内部会转化成
然后动态加载
<script type="text/javascript" src="http://192.168.1.114/yii/demos/test.php?backfunc=jQuery2030038573939353227615_1402643146875&action=aaron"></script>
然后php方就会执行backfunc(传递参数);
所以流程就会分二步:
1:针对jsonp的预处理,主要是转化拼接这些参数,然后处理缓存,因为jsonp的方式也是靠加载script所以要关闭浏览器缓存
inspectPrefiltersOrTransports中,当作了jsonp的预处理后,还要在执行inspect(dataTypeOrTransport);的递归,就是为了关闭这个缓存机制
var dataTypeOrTransport = prefilterOrFactory(options, originalOptions, jqXHR);
/**
* 针对jonsp处理
*/
if (typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[dataTypeOrTransport]) {
//增加cache设置标记
//不需要缓存
//dataTypes: Array[2]
// 0: "script"
// 1: "json"
options.dataTypes.unshift(dataTypeOrTransport);
inspect(dataTypeOrTransport);
return false;
} else if (seekingTransport) {
return !(selected = dataTypeOrTransport);
}
具体的预处理的代码
// Detect, normalize options and install callbacks for jsonp requests
// 向前置过滤器对象中添加特定类型的过滤器
// 添加的过滤器将格式化参数,并且为jsonp请求增加callbacks
jQuery.ajaxPrefilter("json jsonp", function(s, originalSettings, jqXHR) { var callbackName,
overwritten,
responseContainer,
// 如果是表单提交,则需要检查数据
jsonProp = s.jsonp !== false && (rjsonp.test(s.url) ?
"url" :
typeof s.data === "string"
&& !(s.contentType || "").indexOf("application/x-www-form-urlencoded")
&& rjsonp.test(s.data) && "data"
); // Handle iff the expected data type is "jsonp" or we have a parameter to set
// 这个方法只处理jsonp,如果json的url或data有jsonp的特征,会被当成jsonp处理
if (jsonProp || s.dataTypes[0] === "jsonp") { // Get callback name, remembering preexisting value associated with it
// s.jsonpCallback时函数,则执行函数用返回值做为回调函数名
callbackName = s.jsonpCallback = jQuery.isFunction(s.jsonpCallback) ?
s.jsonpCallback() :
s.jsonpCallback; // Insert callback into url or form data
// 插入回调url或表单数据
// "test.php?symbol=IBM&callback=jQuery20309245402452070266_1402451299022"
if (jsonProp) {
s[jsonProp] = s[jsonProp].replace(rjsonp, "$1" + callbackName);
} else if (s.jsonp !== false) {
s.url += (ajax_rquery.test(s.url) ? "&" : "?") + s.jsonp + "=" + callbackName;
} // Use data converter to retrieve json after script execution
s.converters["script json"] = function() {
if (!responseContainer) {
jQuery.error(callbackName + " was not called");
}
return responseContainer[0];
}; // force json dataType
// 强制跟换类型
s.dataTypes[0] = "json"; // Install callback
// 增加一个全局的临时函数
overwritten = window[callbackName];
window[callbackName] = function() {
responseContainer = arguments;
}; // Clean-up function (fires after converters)
// 在代码执行完毕后清理这个全部函数
jqXHR.always(function() {
// Restore preexisting value
window[callbackName] = overwritten; // Save back as free
if (s[callbackName]) {
// make sure that re-using the options doesn't screw things around
s.jsonpCallback = originalSettings.jsonpCallback; // save the callback name for future use
oldCallbacks.push(callbackName);
} // Call if it was a function and we have a response
if (responseContainer && jQuery.isFunction(overwritten)) {
overwritten(responseContainer[0]);
} responseContainer = overwritten = undefined;
}); // Delegate to script
return "script";
}
});
jquery会在window对象中加载一个全局的函数,当代码插入时函数执行,执行完毕后就会被移除。同时jquery还对非跨域的请求进行了优化,如果这个请求是在同一个域名下那么他就会像正常的Ajax请求一样工作。
分发器执行代码:
当我们所有的参数都转化好了,此时会经过请求发送器用来处理发送的具体
为什么会叫做分发器,因为发送的请求目标
ajax因为参杂了jsonp的处理,所以实际上的请求不是通过 xhr.send(XmlHttpRequest)发送的
而是通过get方式的脚本加载的
所以
transports对象在初始化构件的时候,会生成2个处理器
- *: Array[1] 针对xhr方式
- script: Array[1] 针对script,jsonp方式
所以
transport = inspectPrefiltersOrTransports(transports, s, options, jqXHR);
那么得到的transport就会根据当前的处理的类型,来选择采用哪种发送器(*、script)
针对script的请求器
jQuery.ajaxTransport("script", function(s) {
// This transport only deals with cross domain requests
if (s.crossDomain) {
var script, callback;
return {
send: function(_, complete) {
script = jQuery("<script>").prop({
async: true,
charset: s.scriptCharset,
//"http://192.168.1.114/yii/demos/test.php?backfunc=jQuery20308569577629677951_1402642881663&action=aaron&_=1402642881664"
src: s.url
}).on(
"load error",
callback = function(evt) {
script.remove();
callback = null;
if (evt) {
complete(evt.type === "error" ? 404 : 200, evt.type);
}
}
);
document.head.appendChild(script[0]);
},
abort: function() {
if (callback) {
callback();
}
}
};
}
});
此时就很明了吧
所以最终的实现就是通过动态加载脚本!
jQuery源码分析系列(35) : Ajax - jsonp的实现与原理的更多相关文章
- jQuery源码分析系列(36) : Ajax - 类型转化器
什么是类型转化器? jQuery支持不同格式的数据返回形式,比如dataType为 xml, json,jsonp,script, or html 但是浏览器的XMLHttpRequest对象对数据的 ...
- jQuery源码分析系列(37) : Ajax 总结
综合前面的分析,我们总结如下3大块: jQuery1.5以后,AJAX模块提供了三个新的方法用于管理.扩展AJAX请求 前置过滤器 jQuery. ajaxPrefilter 请求分发器 jQuery ...
- jQuery源码分析系列(31) : Ajax deferred实现
AJAX的底层实现都是浏览器提供的,所以任何基于api上面的框架或者库,都只是说对于功能的灵活与兼容维护性做出最优的扩展 ajax请求的流程: 1.通过 new XMLHttpRequest 或其它的 ...
- jQuery源码分析系列(33) : AJAX中的前置过滤器和请求分发器
jQuery1.5以后,AJAX模块提供了三个新的方法用于管理.扩展AJAX请求,分别是: 1.前置过滤器 jQuery. ajaxPrefilter 2.请求分发器 jQuery. ajaxTran ...
- jQuery源码分析系列(34) : Ajax - 预处理jsonp
上一章大概讲了前置过滤器和请求分发器的作用,这一章主要是具体分析每种对应的处理方式 $.ajax()调用不同类型的响应,被传递到成功处理函数之前,会经过不同种类的预处理(prefilters). 预处 ...
- jQuery源码分析系列(30) : Ajax 整体结构
开头引用一段 想起一句话:前端研究,研究个屁~ 的确如此呀.补充下联:前端设计,设计个屁~ 前端目前最大的困境是,如 HTML 一样,无论你承不承认,市场上并不太需要 HTML 高手 其实这里引发一个 ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
- jQuery源码分析系列(转载来源Aaron.)
声明:非本文原创文章,转载来源原文链接Aaron. 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAa ...
随机推荐
- 【转】Oracle索引的类型
数据库的应用类型分为 OLTP(OnLine Transaction Processing ,联机事务处理):OLTP是传统关系型数据库的主要应用,其主要面向基本的.日常的事务处理,例如银行交易. O ...
- Hadoop各商业发行版之比较
Hadoop的发行版除了社区的Apache hadoop外,cloudera,hortonworks,mapR,EMC,IBM,INTEL,华为等等都提供了自己的商业版本.商业版主要是提供了专业的技术 ...
- C++ 结构体数组回调C#代码,c#数组只有一条
C# 方法 [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall, CharSet = CharSet.Ansi)] public ...
- iDB是如何运转的 一
郑昀 创建于2015/12/2 最后更新于2015/12/4 关键词:数据库,MySQL,自动化运维,DDL,DML,SQL审核,备份,回滚,Inception,osc 提纲: 普通DBA和文艺DBA ...
- 【Telerik】<telerik:RadComboBox>导出列表数据
近来在做项目,做到导出功能.使用<telerik:RadComboBox>的下拉框来实现导出部分或导出所有数据的功能.
- 李洪强经典面试题152-Runtime
李洪强经典面试题152-Runtime Runtime Runtime是什么 Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码, ...
- 嵌入式linux下如何尽快播放开机音乐
今天在考虑如何尽快启动一个应用程序,播个开机音乐什么的. 最开始的启动流程是这样的,bootloader 启动kernel,kernel跑完挂载文件系统, 然后会执行/init,而这个init 是指向 ...
- 全选或反选表格中第一列的checkbok
<input type="checkbox" onclick="$('table tr > td:first-child input:checkbox').p ...
- WPF整理-二进制资源和内容
WPF中的Binary Resource(二进制资源)是相对于前面所说的Logical resource(逻辑资源)而说的,一般指Image.XML文件等. 注意:这里说的是Resource" ...
- Spring缓存框架原理浅谈
运维在上线,无聊写博客.最近看了下Spring的缓存框架,这里写一下 1.Spring 缓存框架 原理浅谈 2.Spring 缓存框架 注解使用说明 3.Spring 缓存配置 + Ehcache(默 ...