沉淀一下跨域、ajax、jsonp的知识。
首先了解一下什么是同域、同源策略、跨域。
同域请求和跨域请求的区别在于URL的三要素:协议、域名、端口
同域请求:一个请求的url三要素与当前页面的三要素相同的请求叫做同域请求
跨域请求:一个请求的url三要素与当前页面的三要素有一个不相同的请求就叫做跨域请求
同源策略:它是一种浏览器厂商为了安全,强制添加的一种安全限制,它限制了js在哪些地方(同域请求中)可以用,哪些地方(跨域请求中)不可以用
所以说Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务,只要是跨域请求,一律不准;
但是,我们经常使用外链式的js、img、iframe,我们发现这些也没有在同域下,但是照样能获得数据文件,所以说只要有src属性的都可以安全的跨过同源策略的限制,但是有一点,比如说<script>中的src,如果你的url地址返回的数据不是一个标准的脚本文本,就会报错。
比较img script iframe 这三个标签的特点
img的特点
1、不受同源策略的限制
2、会把加载过来的内容强制当成图片来显示,如果不是合法图片,则显示裂图
iframe的特点
1、加载跨域资源是不受同源策略的限制
2、数据可以成功加载,但是由于同域策略的限制无法获得里面的内容
script特点
1、不受同源策略的限制
2、会把加载过来的内容强制当做脚本来执行,如果返回的数据不是合法的脚本,则执行出错
于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、HTML5中的Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;与此同时我们还知道json字符串是可以直接被原生js解析的,这样就生成了一个方案:web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。
客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。
jsonp的原理
1、利用script标签实现跨域请求
2、server定义好的那个用来设置返回时数据中执行函数的函数名的那个参数
3、jsonpcallback后面跟的value必须是全局作用域下的一个函数
4、server返回的额数据格式是固定的 functionName(data)
jsonp的注意事项:
1、因为jsonp是通过script的src属性去加载跨域资源,所以jsonp请求全部都是get方法请求
2、get系方法有的特点jsonp都有
3、所有的jsonp接口必须含有一个jsonpcallback。否则不是合法的jsonp接口。
4、所有的jsonp接口必须按照格式返回functionName(/*json data*/)
jsonp为什么不是ajax?
因为ajax是通过浏览器操作http请求的API来实现的,而jsonp是通过script实现的,所以jsonp不是ajax。
简单的demo:
页面中的js代码:
// 得到航班信息查询结果后的回调函数
var flightHandler = function(data){
alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');
};
// 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
//这里我将全局函数flightHandler作为参数拼接到url中,为了是服务器返回和此参数相同的函数执行代码,同时将需要返回的参数传到执行方法中,返回格式是flightHandler({"CA1998":"1000"})
var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&jsonpcallback=flightHandler";
// 创建script标签,设置其属性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把script标签加入head,此时调用开始
document.getElementsByTagName('head')[0].appendChild(script);
上述代码,如果你是和跨域的服务器协商好的,那么他会按照你的url地址中的jsoncallback参数,返回以参数值(data)的方式,他会返回一个js或者json文件,里面的代码是flightHandler(data),所以获取到之后就可以直接执行了。
以上是这就是最基本的原理,但是,一个页面中一般不止一个地方会用到跨域请求,但是如果把全局函数写成固定值的话,多次请求的话会发生覆盖的问题,所以优化一下代码,封装一个jsonp.js
(function () {
/**http://suggestion.baidu.com/su?wd=a&cb=window.jsonp.cb2 * jsonp请求
* @param {string} url jsonp地址
* @param {*} data 发送的数据
* @param {string} jsonpcallback jsonpcallback
* @param {Function} callback 回调函数
*/
//http://suggestion.baidu.com/su?wd=a&cb=window.jsonp.cb2
this.jsonp = function (url, data, jsonpcallback, callback) {
// 回调函数名,因为多次调用,名字最好不重复,避免覆盖问题,但是同时每次更改函数名的时候,请求的url地址中的函数名称也得改
var cbName = 'cb' + counter++; //cb1 cb2 cb3
// 构造全局函数名 放到jsonpcallback后面的
var callbackName = 'window.jsonp.' + cbName;
//window.jsonp.cb1
//window.jsonp.cb2
// 根据全局函数名 定义一个全局函数
window.jsonp[cbName] = function (data) {
try {
callback(data);
} finally {
script.parentNode.removeChild(script);//为了避免占用内存,每次完成之后就把script标签删除掉就好了
delete window.jsonp[cbName];//同理将全局函数也一块删除掉
}
};
// 往url后拼接参数
var src = tools.padStringToURL(url, data);
// 往url后拼接jsonpcallback
src = tools.padStringToURL(src, jsonpcallback + '=' + callbackName);
// 动态生成script标签并添加到html中
var script = document.createElement('script');
script.async = 'async';
script.type = 'text/javascript';
script.src = src;
document.documentElement.appendChild(script);
};
// 计数器 每次调用jsonp方法 都累加1
var counter = 1;
var tools = {
//将参数拼接到url之后
padStringToURL: function (url, param) {
param = this.encodeToURIString(param);
if (!param) {
return url;
}
return url + (/\?/.test(url) ? '&' : '?') + param;
},
//将数据转为querystring的格式
encodeToURIString: function (data) {
if (!data) {
return '';
}
if (typeof data === 'string') {
return data;
}
var arr = [];
for (var n in data) {
if (!data.hasOwnProperty(n)) continue;
arr.push(encodeURIComponent(n) + '=' + encodeURIComponent(data[n]));
}
return arr.join('&');
}
}
})();
以上是封装的jsonp.js
html中调用的方法
jsonp('http://suggestion.baidu.com/su',{wd: word}, 'cb', function (data) {callback(data);});
这个url是获得百度搜索的接口,传给一个参数:wd:你要搜索的词 ,百度就会返回一个包含搜索结果的数组,到此,跨域获取数据就完成了
文章评论