解决Ajax 跨域问题 - JSONP原理解析

解决Ajax 跨域问题 - JSONP原理解析

为什么会有跨域问题? - 因为有同源策略

  1. 同源策略是浏览器的一种安全策略,所谓同源指的是 请求URL地址中的 协议, 域名 端口 都相同,只要其中之一不相同就是跨域
  2. 同源策略主要为了保证浏览器的安全性
  3. 在同源策略下,浏览器**不允许**Ajax跨域获取服务器数据
    http://www.example.com/detail.html
    跨域请求:
        http://api.example.com/detail.html 域名不同
        http://www.example.com:8080/detail.html 端口不同
        http://api.example.com:8080/detail.html 域名、端口不同
        https://api.example.com/detail.html  协议、域名不同
        https://www.example.com:8080/detail.html 端口、协议不同

使用 jquery ajax-JSONP方式调用 解决跨域

虽然 jsonp 的实现跟 ajax 没有半毛钱关系jsonp是通过 script的src实现的(具体看后面的解析),但是最终目的都是向服务器请求数据然后回调,而且为了方便,所以jquery把 jsonp 也封装在了 $.ajax 方法中,调用方式与 ajax 调用方式略有区别,案例如下。

  • 测试jq_jsonp:

    目标请求地址, http://www.jepson.com/jq_jsonp.php

        <?php
            $cb = $_GET[ ‘callback‘ ];    // 获取回调
            $arr = array( ‘username‘ => ‘zhangsan‘, ‘password‘ => ‘123456‘ );
            echo $cb.‘(‘. json_encode($arr) .‘);‘; // 返回回调函数调用
    
            // 就是 jQuery1111011117830135968942_1478857963357({"username":"zhangsan","password":"123456"});
        ?>

    在 peter.com/jq_jsonp.html 里面,通过 Jquery ajax 方法, JSONP 方式调用,进行请求

        <script type="text/javascript" src="./jquery.js"></script>
        <script type="text/javascript">
            $(function(){
                $("#btn").click(function(){
                    $.ajax({
                        type:‘get‘,
                        url:‘http://www.jepson.com/jq_jsonp.php‘,
                        dataType:‘jsonp‘,
                        jsonp: ‘callback‘, // 回调函数,后台get接收的变量名,默认 就callback
                        // jsonpCallback: ‘abc‘,    回调函数名,默认是一长串,jquery自动生成的,在请求url中可以看到
                                                // 改不改无所谓,改短一点也只是 查看请求url的时候清晰一点而已
                        success:function(data){
                            console.log(data.username,data.password);   // zhangsan 123456
                        },
                        error:function(data){
                            console.dir(data);
                            console.log(‘error‘);
                        }
                    });
                });
            });
        </script>
        <body>
            <input type="button" value="点击" id="btn">
        </body>

    可以看到,输出了 ‘zhangsan 123456’。

    注意:若后台没做获取回调,返回函数调用的工作,那么就会出错,进入 error方法。这是工作中常会遇到的错误,这里要注意


解决方式 JSONP 的原理解析

JSONP的原理:利用 script标签 的 src属性 进行跨域请求,服务器响应函数调用传参

这里了解 JSONP的原理 并进行测试,需要不同域,我这里是 apache 服务器,自己配置了 两个虚拟主机 做的。

两个域 代码所在域 http://www.peter.com/, 请求域 http://www.jepson.com/


JSONP基本原理 - 静态方法创建

我们通过下面静态方法创建的测试来看看JSONP解决跨域问题的基本原理

- 测试1:

目标请求地址,http:/www.jepson.com/1.php
      <?php
          echo "1;";
       ?>
在 peter.com/1.html 里面,通过 script标签进行请求
    <script type="text/javascript" src="http://www.jepson.com/1.php"></script>

打开F12,进入 network,找到 1.php的那个请求,选中 Response,发现结果是 1,跨域请求就这么成功了! 没错原理就是这么简单。

但是有没有发现,我们是无法获得这个请求到的数据的,怎么办?

script标签默认是同步加载的,那我们就 echo ‘var a = 1;’ ,获取过来就是‘var a = 1;’, 那是不是就是可以认为声明了一个变量并赋值了?

又因为是同步的,所以后面的 script语句是不是就可以用这个 a 了?我们来试一试

- 测试2:

目标请求地址,http:/www.jepson.com/2.php,就一行
    <?php
        echo "var a = 1;";
     ?>

在 peter.com/2.html 里面,通过 script标签进行请求

    <!--  我是不是可以理解为这里声明了一个变量? var a = 1; -->
    <script type="text/javascript" src="http://www.jepson.com/2.php"></script>
    <script type="text/javascript">
        console.log( a );   // 1
    </script>

F12 打开控制台,没有错!你输出 a 了, 输出的值为 1,我们拿到了后台传输的数据

但是,这种静态的方式明显有很多弊端,首先要放在代码的顶部,不然下面的没法用到返回的数据

其次,这种静态的方式传参配置很不方便,于是乎我们一般采用 动态创建 script标签的方式,添加到头部,并配参数


JSONP基本原理 - 动态方法创建

动态创建 script标签的方式,添加到头部,并配参数,可以解决金泰

- 测试3:

目标请求地址,http:/www.jepson.com/3.php,就一行
    <?php
        echo "var a = 1;";
     ?>
在 peter.com/3.html 里面,通过 script语句动态创建 script标签进行请求
       <script type="text/javascript">
           var script = document.createElement(‘script‘);
           script.src = ‘http://www.jepson.com/3.php‘;
           var head = document.getElementsByTagName(‘head‘)[0];
           head.appendChild(script);
           console.log( a );
       </script>

打开 F12,令人震惊的事情发生了,** 居然报错了,**Uncaught ReferenceError: a is not defined(…) ?? a变量没有 定义?

我们打开 network,找到 1.php这个请求,点开response,发现有 ‘var a = 1;’,请求成功了呀?这是什么情况?

注意注意:原来动态创建 script 的方式发送请求 是异步的,虽然请求成功了,但是在使用变量时,请求还没有完成,相当于还没有定义变量,然后就报错了。

那怎么办?这里有一个小技巧, 我们可以 echo ‘foo( 123 )’; 这样相当于请求完毕执行 foo(123),即调用我们 代码中foo函数,我们在代码中写一个函数 function foo( data ) { console.log( data ); } 这就相当于进行了回调,我们拿到了数据。下面是代码演示。

  • 测试4:

    目标请求地址,http:/www.jepson.com/4.php

        <?php
            echo ‘foo(123)‘;
         ?>

    在 peter.com/4.html 里面,通过 script语句动态创建 script标签进行请求

        <script type="text/javascript">
            var script = document.createElement(‘script‘);
            script.src = ‘http://www.jepson.com/4.php‘;
            var head = document.getElementsByTagName(‘head‘)[0];
            head.appendChild(script);
    
            function foo(data){
                console.log( data );    //foo(123) 对 foo 调用 输出 123
            }
        </script>

    F12 可以看到 输出 123 了, 并且在network中查看请求,response 可以看到是 foo(123)

    那我们就可以用这种动态的方式 很轻松的 拿到后台数据了,只不过前台声明的和 后台 调用的 函数名 需要一样才行,如上面的 foo,这样就不太好了,每次改动,那都要对接一下。

    所以我们可以把回调函数名放在参数中传输。案例如下:

  • 测试5:

    目标请求地址,http:/www.jepson.com/5.php

        <?php
            $cb = $_GET[ ‘callback‘ ];    // get 通过 callback键 得到 函数名
            $arr = array( ‘username‘ => ‘zhangsan‘, ‘password‘ => ‘123456‘ );// 我们也可以传复杂一点的数据
            echo $cb.‘(‘. json_encode($arr) .‘);‘;// hello( json_encode($arr) )
        ?>

    在 peter.com/5.html 里面,通过 script语句动态创建 script标签进行请求

        <script type="text/javascript">
            function hello(data){
                console.log(data);// Object {username: "zhangsan", password: "123456"}
                console.log(data.username); // zhangsan
                console.log(data.password); // 123456
            }
            var script = document.createElement(‘script‘);
            script.src = ‘http://jepson.com/5.php?callback=hello‘;
            var head = document.getElementsByTagName(‘head‘)[0];
            head.appendChild(script);
        </script>

**总结:**jsonp的本质:动态创建script标签,然后通过它的src属性发送跨域请求,然后服务器端响应的数据格式为【函数调用】,所以在发送请求之前必须先声明一个函数,并且函数的名字与参数中传递的名字要一致。这里声明的函数是由服务器响应的内容(实际就是一段函数调用js代码)来调用。JSONP是一个协议。


简单封装

上面就是 JSONP 的全部原理了,下面是 学习中实现的 jquery ajax JSONP 方式的 简单封装,感兴趣的可以参考一下。

    function ajax( obj ){
        // 默认参数 由于 jsonp 原理是 在 url体 中传递,
        // 所以仅支持 get, 所以不对 type 进行配置
        var defaults = {
            url : ‘#‘,
            dataType : ‘jsonp‘,
            jsonp : ‘callback‘,
            data : {},
            success : function( data ) { console.log( data ) }
        }

        // 处理形参,传递函数的时候就覆盖默认参数,不传递就使用默认的参数
        for ( var key in obj ) {
            defaults[ key ] = obj[ key ];
        }

        // 这里是默认的回调函数名称,根据当前日期和随机数生成
        var cbName = ‘jQuery‘ + (‘1.11.1‘ + Math.random()).replace(/\D/g,"") + ‘_‘ + (new Date().getTime());// 去掉所有的小点,相当于 jquery 后面加一串数字_再加上时间数字
        if( defaults.jsonpCallback ){
            cbName = defaults.jsonpCallback;
        }

        // 这里就是回调函数,调用方式:服务器响应内容来调用
        // 向window对象中添加了一个方法,方法名称是变量cbName的值
        window[ cbName ] = function( data ){
            defaults.success( data );//这里success的data是实参
        }

        // 将所传参数 data 进行处理,添加到 src url中
        var param = ‘‘;
        for( var attr in defaults.data ){
            param += attr + ‘=‘ + defaults.data[ attr ] + ‘&‘;
        }
        if( param ){  // 去掉最后的一个 &
            param = param.substring( 0, param.length-1 );
            param = ‘&‘ + param;
        }

        // 动态添加 script 标签, 配置参数
        var script = document.createElement( ‘script‘ );
        // defaults.jsonp 后台 get 变量名,cbName 回调函数名, param 变量
        script.src = defaults.url + ‘?‘ + defaults.jsonp + ‘=‘ + cbName + param;
        var head = document.getElementsByTagName( ‘head‘ )[ 0 ];
        head.appendChild( script );
    }
时间: 2024-12-10 17:02:37

解决Ajax 跨域问题 - JSONP原理解析的相关文章

Ajax跨域:Jsonp原理解析

推荐先看下这篇文章:JS跨域(ajax跨域.iframe跨域)解决方法及原理详解(jsonp) JavaScript是一种在Web开发中经常使用的前端动态脚本技术.在JavaScript中,有一个很重要的安全性限制,被称为“Same-Origin Policy”(同源策略).这一策略对于JavaScript代码能够访问的页面内容做了很重要的限制,即JavaScript只能访问与包含它的文档在同一域下的内容. JavaScript这个安全策略在进行多iframe或多窗口编程.以及Ajax编程时显得

解决ajax跨域的方法原理详解之Cors方法

1.神马是跨域(Cross Domain) 对于端口和协议的不同,只能通过后台来解决. 一句话:同一个ip.同一个网络协议.同一个端口,三者都满足就是同一个域,否则就是 跨域问题了.而为什么开发者最初不直接定为一切可跨域的呢?默认的为什么都是不可跨域呢?这就涉及到了同源策 略,为了系统的安全,由Netscape提出一个著名的安全策略.现在所有支持JavaScript的浏览器都会使用这个策略. 所谓同源是,域名,协议,端口相同.当我们在浏览器中打开百度和谷歌两个网站时,百度浏览器在执行一个脚本的

深入浅出JSONP:解决AJAX跨域问题

本文主要讲解了如何去解决AJAX跨域的问题,从跨域的简单原理到JONP实现模式详细的讲解整个解决方案,最后利用jQuery可以很方便的实现JSONP来进行跨域访问. 上周客户新买了服务器,原本在旧的服务器上放着客户的Web主页信息和一个后台程序(asp.net),在客户的主页中有一个动态显示最新消息的处理,这个处理就是通过ajax异步从那个后台程序中取得的.由于又购买了新的服务器,客户想把web主页和那个后台程序分开来,后台程序被部署到了新的服务器上.不过这个项目是我的同事小福同志开发的,也就由

jQuery中利用JSONP解决AJAX跨域问题

写在前面 跨域的解决方案有多种,其中最常见的是使用同一服务器下的代理来获取远端数据,再通过ajax进行读取,而在这期间经过了两次请求过程,使得获取数据的效率大大降低,这篇文章蓝飞就为大家介绍一下解决跨域问题的一种比较通用的方案——JSONP. 什么是跨域? 简单的来说,出于安全方面的考虑,页面中的JavaScript无法访问其他服务器上的数据,即“同源策略”.而跨域就是通过某些手段来绕过同源策略限制,实现不同服务器之间通信的效果. 具体策略限制情况可看下表: URL 说明 允许通信 http:/

Ajax 跨域请求 jsonp获取json数据

遇到Ajax的跨域请求出问题 找了中解决办法如下: 参考内容:http://justcoding.iteye.com/blog/1366102 由于受到浏览器的限制,该方法不允许跨域通信.如果尝试从不同的域请求数据,会出现安全错误.如果能控制数 据驻留的远程服务器并且每个请求都前往同一域,就可以避免这些安全错误.但是,如果仅停留在自己的服务器上,Web 应用程序还有什么用处呢?如果需要从多个第三方服务器收集数据时,又该怎么办? 理解同源策略 同源策略阻止从一个域上加载的脚本获取或操作另一个域上的

解决Ajax跨域问题:Origin xx is not allowed by Access-Control-Allow-Origin.

解决Ajax跨域问题:Origin xx is not allowed by Access-Control-Allow-Origin. 今天一个Ajax跨域问题,纠结我半天,记录之. <html> <head> <title>title</title> <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script> <script>

如何解决ajax跨域问题(转)

最近项目中用到json作为系统间交互的手段,自然就伴随着众多ajax请求,随之而来的就是要解决 ajax的跨域问题.本篇将讲述一个小白从遇到跨域不知道是跨域问题,到知道是跨域问题不知道如何解决,再到解决跨域问题,最后找到两种方法解决ajax 跨域问题的全过程. 不知是跨域问题 起 因是这样的,为了复用,减少重复开发,单独开发了一个用户权限管理系统,共其他系统获取认证与授权信息,暂且称之为A系统:调用A系统以B为例.在B系统 中用ajax调用A系统系统的接口(数据格式为json),当时特别困惑,在

如何解决ajax跨域问题

如何解决ajax跨域问题(转) 由 于此前很少写前端的代码(哈哈,不合格的程序员啊),最近项目中用到json作为系统间交互的手段,自然就伴随着众多ajax请求,随之而来的就是要解决 ajax的跨域问题.本篇将讲述一个小白从遇到跨域不知道是跨域问题,到知道是跨域问题不知道如何解决,再到解决跨域问题,最后找到两种方法解决ajax 跨域问题的全过程. 不知是跨域问题 起 因是这样的,为了复用,减少重复开发,单独开发了一个用户权限管理系统,共其他系统获取认证与授权信息,暂且称之为A系统:调用A系统以B为

如何解决ajax跨域请求?

1.什么是跨域? a.域名不同 b.域名相同,端口不同 注:只有域名相同,端口相同时,才可以访问数据 可以使用jsonp解决ajax跨域请求的问题. 2.什么是jsonp? Jsonp其实就是一个跨域解决方案.Js跨域请求是不可以的,但是js跨域请求js脚本是可以的.可以把数据封装成一个js语句,做一个方法的调用.跨域请求js脚本可以得到此脚本.得到js脚本之后会立即执行.可以把数据作为参数传递到方法中.就可以获得数据.从而解决跨域请求的问题. 原文地址:https://www.cnblogs.