愚墨的博客
  • 首页
  • 前端技术
  • 面试
只争朝夕不负韶华
  1. 首页
  2. 前端技术
  3. 正文

jsonp跨域问题

2016年05月28日 2800点热度 0人点赞 0条评论

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

DOME

标签: 暂无
最后更新:2016年07月27日

愚墨

保持饥渴的专注,追求最佳的品质

点赞
< 上一篇
下一篇 >

文章评论

取消回复

搜搜看看
历史遗迹
  • 2023年5月
  • 2022年9月
  • 2022年3月
  • 2022年2月
  • 2021年12月
  • 2021年8月
  • 2021年7月
  • 2021年5月
  • 2021年4月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年9月
  • 2020年7月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年1月
  • 2019年5月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年9月
  • 2018年3月
  • 2018年2月
  • 2018年1月
  • 2017年11月
  • 2017年7月
  • 2017年6月
  • 2017年3月
  • 2017年2月
  • 2017年1月
  • 2016年12月
  • 2016年11月
  • 2016年9月
  • 2016年8月
  • 2016年7月
  • 2016年6月
  • 2016年5月
  • 2016年4月
  • 2016年3月
  • 2016年2月
  • 2016年1月
  • 2015年12月
  • 2015年10月
  • 2015年9月
  • 2015年7月
  • 2015年6月
  • 2015年4月

COPYRIGHT © 2020 愚墨的博客. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS