沉淀一下跨域、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:你要搜索的词 ,百度就会返回一个包含搜索结果的数组,到此,跨域获取数据就完成了
文章评论