ajax跨域、jsonp原理

Ajax跨域

  • 同源策略

    • 同源策略是浏览器的一种安全策略,所谓同源指的是请求URL地址中的协议、域名和端口都相同,只要其中之一不相同就是跨域。

    • 同源策略主要为了保证浏览器的安全性。

    • 在同源策略下,浏览器不允许Ajax跨域获取服务器数据。

http://www.example.com/detail.html

说明
http://api.example.com/detail.html 域名不同
https://www.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 端口、协议不同

跨域解决方案

  • jsonp (仅仅是get请求方式)

  • document.domain+iframe

  • location.hash + iframe

  • window.name + iframe

  • window.postMessage

  • flash等第三方插件

jsonp 原理

  • 静态script标签的src属性进行跨域请求

    <script src="http://www.ajaxDemo.cc/check.php"></script> <!-- 同步加载 -->
    <!--<script src="http://www.ajaxDemo.cc/check.php" async></script>--> <!-- async 异步加载 -->
    <script >
        /*
            这种方式存在的两个主要的问题:
            1、必须保证加载的顺序
            2、不方便通过程序传递参数
            */
        console.log(data)
    </script>
    
    
  • 动态创建script标签,通过标签的src属性发送请求(异步请求)

    <script>
        var script = document.createElement('script');
        script.src = 'http://www.ajaxDemo.cc/check.php?callback=foo';
    
        var head = document.getElementsByTagName('head')[0];
        head.appendChild(script);
    		
        function foo(data){
            console.log(data)
        }
    </script>
    
    // check.php
    $cb = $_GET['callback'];
    $data = '{"code":"2","msg":"登录失败"}';
    echo "$cb($data)";
    

原理:

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

  • 需要注意的是,callback参数定义是需要前后端定义好的,具体什么名字,商讨好就可以了

jQuery 对 jsonp 的支持

$.ajax({
    url: 'http://www.ajaxDemo.cc/check.php',
    type:'get',
    dataType: 'jsonp', //指定服务器返回的数据类型
    jsonp:'cb', //指定参数名称,默认是callback
    jsonpCallback:'abc', //指定回调函数名称,默认是 "jQuery" + ( version + Math.random() ).replace( /\D/g, "" )+当前时间毫秒数
    data:{},
    success: function(res){
        console.log(res)
    },
    error:function(err){
        console.log(err)
    }
})

jsonp 接口

  • 基于jsonp的常见功能

    • 搜索智能提示

    • 快递查询

  • 第三方接口介绍(json/jsonp)

模拟 jQuery jsonp封装可跨域请求

function ajax(obj){
    var defaults = {
        type : 'get',
        async : true,
        url : '#',
        dataType : 'text',
        jsonp : 'callback',
        data : {},
        success:function(data){console.log(data);}
    }

    for(var key in obj){
        defaults[key] = obj[key];
    }

    if(defaults.dataType == 'jsonp'){
        ajax4Jsonp(defaults);
    }else{
        ajax4Json(defaults);
    }
}

function ajax4Json(defaults){
    // 1、创建XMLHttpRequest对象
    var xhr = null;
    if(window.XMLHttpRequest){
        xhr = new XMLHttpRequest();
    }else{
        xhr = new ActiveXObject('Microsoft.XMLHTTP');
    }
    // 把对象形式的参数转化为字符串形式的参数
    /*
    {username:'zhangsan','password':123}
    转换为
    username=zhangsan&password=123
    */
    var param = '';
    for(var attr in defaults.data){
        param += attr + '=' + defaults.data[attr] + '&';
    }
    if(param){
        param = param.substring(0,param.length - 1);
    }
    // 处理get请求参数并且处理中文乱码问题
    if(defaults.type == 'get'){
        defaults.url += '?' + encodeURI(param);
    }
    // 2、准备发送(设置发送的参数)
    xhr.open(defaults.type,defaults.url,defaults.async);
    // 处理post请求参数并且设置请求头信息(必须设置)
    var data = null;
    if(defaults.type == 'post'){
        data = param;
        xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
    }
    // 3、执行发送动作
    xhr.send(data);
    // 处理同步请求,不会调用回调函数
    if(!defaults.async){
        if(defaults.dataType == 'json'){
            return JSON.parse(xhr.responseText);
        }else{
            return xhr.responseText;
        }
    }
    // 4、指定回调函数(处理服务器响应数据)
    xhr.onreadystatechange = function(){
        if(xhr.readyState == 4){
            if(xhr.status == 200){
                var data = xhr.responseText;
                if(defaults.dataType == 'json'){
                    // data = eval("("+ data +")");
                    data = JSON.parse(data);
                }
                defaults.success(data);
            }
        }
    }
}
function ajax4Jsonp(defaults){
    // 这里是默认的回调函数名称
    // expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
    var cbName = 'jQuery' + ('1.11.1' + Math.random()).replace(/\D/g,"") + '_' + (new Date().getTime());
    if(defaults.jsonpCallback){
        cbName = defaults.jsonpCallback;
    }

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

    var param = '';
    for(var attr in defaults.data){
        param += attr + '=' + defaults.data[attr] + '&';
    }
    if(param){
        param = param.substring(0,param.length-1);
        param = '&' + param;
    }
    var script = document.createElement('script');
    script.src = defaults.url + '?' + defaults.jsonp + '=' + cbName + param;
    var head = document.getElementsByTagName('head')[0];
    head.appendChild(script);
}

基本使用:

ajax({
    url:'',
    type:'get',
    data:{},
    dataType:'jsonp'
    success: function(res){
    	console.log(res)
	}
})
上一篇:react中使用jsonp跨域


下一篇:Javascript-在使用jQuery请求JSONP时可以使用静态(即预定的)回调函数名称吗?