前端通信跨域

Posted lhh520

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端通信跨域相关的知识,希望对你有一定的参考价值。

首先了解什么是同源策略:

限制一个源加载的文档或脚本与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。(来自MDN的解释)

源包括三个部分:协议、域名、端口(HTTP协议的默认端口是80)。如果其中有任何一个部分不同,则源不同。即为跨域。

限制:这个源的文档没有权利去操作另一个源的文档。这个限制体现在:

Cookie、LlocalStorage和IndexDB无法获取;

无法获取和操作DOM;

不能发送Ajax请求。(Ajax只适合同源的通信)。

 

前后端的通信方式:

Ajax:不支持跨域;

WebSocket:不受同源策略的限制,支持跨域;

CORS:不受同源策略的限制,支持跨域;一种新的通信协议标准。可以理解成同时支持同源和跨域的Ajax。

 

如何创建Ajax:

在回答 Ajax 的问题时,要回答以下几个方面:

  • 1、XMLHttpRequest 的工作原理

  • 2、兼容性处理

XMLHttpRequest只有在高级浏览器中才支持。在回答问题时,这个兼容性问题不要忽略。

  • 3、事件的出发条件

  • 4、事件的触发顺序

XMLHttpRequest有很多触发事件,每个事件是怎么触发的。

发送Ajax请求的五个步骤(XMLHttpRequest的工作原理)

1.创建XMLHttpRequest对象;

2.使用open方法设置请求的参数。open(method,url,是否异步)

3.发送请求;

4.注册事件。使用onreadystatechange事件,状态改变时就会调用。如果要在数据完整请求返回才调用需要加一些状态判断;

5.获取返回的数据,更新UI

发送get请求和post请求:
get请求举例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<h1>Ajax 发送 get 请求</h1>
<input type="button" value="发送get_ajax请求" id=‘btnAjax‘>

<script type="text/javascript">
    // 绑定点击事件
    document.querySelector(‘#btnAjax‘).onclick = function () {
        // 发送ajax 请求 需要 五步

        // (1)创建异步对象
        var ajaxObj = new XMLHttpRequest();

        // (2)设置请求的参数。包括:请求的方法、请求的url。
        ajaxObj.open(‘get‘, ‘02-ajax.php);

        // (3)发送请求
        ajaxObj.send();

        //(4)注册事件。 onreadystatechange事件,状态改变时就会调用。
        //如果要在数据完整请求回来的时候才调用,我们需要手动写一些判断的逻辑。
        ajaxObj.onreadystatechange = function () {
            // 为了保证 数据 完整返回,我们一般会判断 两个值
            if (ajaxObj.readyState == 4 && ajaxObj.status == 200) {
                // 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的页面是存在的
                // 5.在注册的事件中 获取 返回的 内容 并修改页面的显示
                console.log(‘数据返回成功‘);

                // 数据是保存在 异步对象的 属性中
                console.log(ajaxObj.responseText);

                // 修改页面的显示
                document.querySelector(‘h1‘).innerHTML = ajaxObj.responseText;
            }
        }
    }
</script>
</body>
</html>

 

post请求举例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<h1>Ajax 发送 get 请求</h1>
<input type="button" value="发送put_ajax请求" id=‘btnAjax‘>
<script type="text/javascript">

    // 异步对象
    var xhr = new XMLHttpRequest();

    // 设置属性
    xhr.open(‘post‘, ‘02.post.php‘);

    // 如果想要使用post提交数据,必须添加此行
    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

    // 将数据通过send方法传递
    xhr.send(‘name=fox&age=18‘);

    // 发送并接受返回值
    xhr.onreadystatechange = function () {
        // 这步为判断服务器是否正确响应
        if (xhr.readyState == 4 && xhr.status == 200) {
            alert(xhr.responseText);
        }
    };
</script>
</body>
</html>

 

onreadystatechange事件:

注册onreadystatechange事件后,每当readyState属性改变时,就会调用onreadystatechange函数。

readyState:(存有XMLHttpRequest的状态。从0到4变化)

0:请求未初始化;

1:服务器连接已建立;

2:请求已接收;

3:请求处理中;

4:请求已完成,且响应已就绪

 

事件的触发条件

技术分享图片

 

 事件的触发顺序

技术分享图片

 

原生Ajax请求

 var util = {};

    //获取 ajax 请求之后的json
    util.json = function (options) {

        var opt = {
            url: ‘‘,
            type: ‘get‘,
            data: {},
            success: function () {
            },
            error: function () {
            },

        };
        util.extend(opt, options);
        if (opt.url) {
            //IE兼容性处理:浏览器特征检查。检查该浏览器是否存在XMLHttpRequest这个api,没有的话,就用IE的api
            var xhr = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject(‘Microsoft.XMLHTTP‘);

            var data = opt.data,
                url = opt.url,
                type = opt.type.toUpperCase();
            dataArr = [];
        }

        for (var key in data) {
            dataArr.push(key + ‘=‘ + data[key]);
        }

        if (type === ‘GET‘) {
            url = url + ‘?‘ + dataArr.join(‘&‘);
            xhr.open(type, url.replace(/?$/g, ‘‘), true);
            xhr.send();
        }

        if (type === ‘POST‘) {
            xhr.open(type, url, true);
            // 如果想要使用post提交数据,必须添加此行
            xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            xhr.send(dataArr.join(‘&‘));
        }

        xhr.onload = function () {
            if (xhr.status === 200 || xhr.status === 304) { //304表示:用缓存即可。206表示获取媒体资源的前面一部分
                var res;
                if (opt.success && opt.success instanceof Function) {
                    res = xhr.responseText;
                    if (typeof res === ‘string‘) {
                        res = JSON.parse(res);  //将字符串转成json
                        opt.success.call(xhr, res);
                    }
                }
            } else {
                if (opt.error && opt.error instanceof Function) {
                    opt.error.call(xhr, res);
                }
            }
        };
    }

 

跨域通信的几种方式:

1.JSONP

2.WebSocket

3.CORS

4.Hash

5.postMessage

 

1.JSONP

原理:通过<script>标签的异步加载来实现。例如通过<script>标签的src,里面放url,加载一些插件,就用到JSONP。

实现:<script src="http://www.smyhvae.com/?data=name&callback=myjsonp"></script> 

上面的src中,data=name是get请求的参数,myjsonp是和后台约定好的函数名。

服务器端这样写:

myjsonp({
            data: {}

        })

于是,本地要求创建一个myjsonp 的全局函数,才能将返回的数据执行出来。

实际开发中前端的JSONP的实现:

<script>

    var util = {};

    //定义方法:动态创建 script 标签
    /**
     * [function 在页面中注入js脚本]
     * @param  {[type]} url     [description]
     * @param  {[type]} charset [description]
     * @return {[type]}         [description]
     */
    util.createScript = function (url, charset) {
        var script = document.createElement(‘script‘);
        script.setAttribute(‘type‘, ‘text/javascript‘);
        charset && script.setAttribute(‘charset‘, charset);
        script.setAttribute(‘src‘, url);
        script.async = true;
        return script;
    };


    /**
     * [function 处理jsonp]
     * @param  {[type]} url      [description]
     * @param  {[type]} onsucess [description]
     * @param  {[type]} onerror  [description]
     * @param  {[type]} charset  [description]
     * @return {[type]}          [description]
     */
    util.jsonp = function (url, onsuccess, onerror, charset) {
        var callbackName = util.getName(‘tt_player‘); //事先约定好的 函数名
        window[callbackName] = function () {      //根据回调名称注册一个全局的函数
            if (onsuccess && util.isFunction(onsuccess)) {
                onsuccess(arguments[0]);
            }
        };
        var script = util.createScript(url + ‘&callback=‘ + callbackName, charset);   //动态创建一个script标签
        script.onload = script.onreadystatechange = function () {   //监听加载成功的事件,获取数据
            if (!script.readyState || /loaded|complete/.test(script.readyState)) {
                script.onload = script.onreadystatechange = null;
                // 移除该script的 DOM 对象
                if (script.parentNode) {
                    script.parentNode.removeChild(script);
                }
                // 删除函数或变量
                window[callbackName] = null;  //最后不要忘了删除
            }
        };
        script.onerror = function () {
            if (onerror && util.isFunction(onerror)) {
                onerror();
            }
        };
        document.getElementsByTagName(‘head‘)[0].appendChild(script); //往html中增加这个标签,目的是把请求发送出去
    };

</script>

 

2.WebSocket

代码实例如下:

    var ws = new WebSocket(‘wss://echo.websocket.org‘); //创建WebSocket的对象。参数可以是 ws 或 wss,后者表示加密。

    //把请求发出去
    ws.onopen = function (evt) {
        console.log(‘Connection open ...‘);
        ws.send(‘Hello WebSockets!‘);
    };


    //对方发消息过来时,我接收
    ws.onmessage = function (evt) {
        console.log(‘Received Message: ‘, evt.data);
        ws.close();
    };

    //关闭连接
    ws.onclose = function (evt) {
        console.log(‘Connection closed.‘);
    };

 

3.CORS

fetch是一个比较新的API,用来实现CORS通信。用法如下:

// url(必选),options(可选)
      fetch(‘/some/url/‘, {
          method: ‘get‘,
      }).then(function (response) {  //类似于 ES6中的promise

      }).catch(function (err) {
        // 出错了,等价于 then 的第二个参数,但这样更好用更直观
      });

另外,如果面试官问:“CORS为什么支持跨域的通信?”

答案:跨域时,浏览器会拦截Ajax请求,并在http头中加Origin。

 

4.Hash

url的#后面的内容就叫Hash。Hash的改变,页面不会刷新。这就是用 Hash 做跨域通信的基本原理。

补充:url的?后面的内容叫Search。Search的改变,会导致页面刷新,因此不能做跨域通信。

场景:我的页面 A 通过iframe或frame嵌入了跨域的页面 B。

现在,我这个A页面想给B页面发消息,怎么操作呢?

(1)首先,在我的A页面中:

 //伪代码
    var B = document.getElementsByTagName(‘iframe‘);
    B.src = B.src + ‘#‘ + ‘jsonString‘;  //我们可以把JS 对象,通过 JSON.stringify()方法转成 json字符串,发给 B

(2)然后,在B页面中:

// B中的伪代码
    window.onhashchange = function () {  //通过onhashchange方法监听,url中的 hash 是否发生变化
        var data = window.location.hash;
    };

 

5.postMessage()方法

场景:窗口 A (http:A.com)向跨域的窗口 B (http:B.com)发送信息。步骤如下。

(1)在A窗口中操作如下:向B窗口发送数据:

// 窗口A(http:A.com)向跨域的窗口B(http:B.com)发送信息
    Bwindow.postMessage(‘data‘, ‘http://B.com‘); //这里强调的是B窗口里的window对象

(2)在B窗口中操作如下:

// 在窗口B中监听 message 事件
    Awindow.addEventListener(‘message‘, function (event) {   //这里强调的是A窗口里的window对象
        console.log(event.origin);  //获取 :url。这里指:http://A.com
        console.log(event.source);  //获取:A window对象
        console.log(event.data);    //获取传过来的数据
    }, false);

 

 

参考:

https://www.cnblogs.com/smyhvae/p/8523576.html


以上是关于前端通信跨域的主要内容,如果未能解决你的问题,请参考以下文章

前端 - 跨域

关于ajax跨域通信-博客园老牛大讲堂

解决vue跨域axios异步通信

前端跨域的解决方式

前端跨域问题

前端跨域解决方案