• 跨域问题解决方案及对应的问题


    1. 窗口(iframe)通信

    不同源的网页,可以获取窗口的以下属性和方法:

    window.closed
    window.frames
    window.length
    window.location
    window.opener
    window.parent
    window.self
    window.top
    window.window
    window.blur()
    window.close()
    window.focus()
    window.postMessage()

    1. document.domain

    场景: 给不同源(一级域名+端口+协议相同)的网页设置:

    document.domain = '一级域名';

    如:image.baidu.com和video.baidu.com就可以通过这个方法。

    则可以通过js读取彼此的Cookie, 对于iframe窗口可以获取彼此的DOM。

    对于服务器通过Set-Cookie返回的Cookie, 可以使其domain属性等于其一级域名。

    示例:

    // a页面 http://a.lyra.com:3000/a.html
    <iframe id="iframeEle"
      src="http://a.lyra.com:3000/c.html">
    </iframe>
    <script>
      document.domain = 'lyra.com';
      iframeEle.addEventListener('load', function() {
        console.log(iframe.contentWindow.a);
      })
    </script>
    // c页面 http://c.lyra.com:3000/c.html
    window.a = 3000;

    2. hashChange(url#后面的部分)

    场景: iframe跨窗口通信。

    示例: 父窗口给子窗口设置带#的url;子窗口通过监听hashchange事件,获取#数据。

    // 父窗口
    iframe = document.querySelector('iframe');
    iframe.src = OriginalUrl + '#' + data;
    // 子窗口
    window.onhashchange = function(e) {
       // data = window.location.hash
    }

    通过子窗口修改父窗口的#部分,会报错,因为跨域

    parent.location.href = OriginalUrl + '#' +data; // Blcok

    此时可以通过一个和父窗口同源的页面b,中转实现:

    /**********************父窗口************************************/
    let firstLoad = true;
    iframeEle.addEventListener('load', function() {
      iframeEle.src += '#hello'
    })
    // 监听b页面的hash修改
    window.onhashchange = function() {
      console.log(window.location.hash); //获取来自子窗口的hash
    }
    /************中转页面;同时也是子窗口的子窗口***********************/ // 同源页面直接修改hash,将c页面的hash值赋值给父窗口 parent.parent.location.hash = window.location.hash; /****************子窗口*****************************************/ window.onhashchange = function(e) { console.log(window.location.hash);// 获得来自父窗口的hash } // 将子窗口的hash传递到父窗口同源b页面,其为子窗口的子窗口 const iframe = document.createElement('iframe'); iframe.src = 'http://localhost:3000/b.html#nohello'; document.body.appendChild(iframe);

    3.window.postMessage-H5

    这是一个跨文档(两个页面之间)通信的API。

    语法:

    otherWindow.postMessage(message, targetOrigin[, transfer]);
    // otherWindow目标窗口
    // message是需要传递的数据
    // targetOrigin是目标源,消息会发送给和这个同源的地址;不确定可以是'*'

    然后在目标页面监听message事件

    window.onmessage = function(e) {
       // e.data
       // e.origin 通过该属性过滤监听的数据
       // e.source
    }

    示例:

    页面1(http://localhost:3000/1.html)

        <iframe id="iframeEle" name="ifr" 
          src="http://localhost:4000/2.html">
        </iframe>
        <script>
          // window.id代表当前DOM节点
          // window.name代表当前iframe窗口
          iframeEle.addEventListener('load', function() {
            ifr.postMessage('hello4000', 'http://localhost:4000');
            // 在这里获取不到ifr;因为跨域。
            // 但是可以获取到其postMessage方法,因为即使跨域也支持获取其postMessage方法。
          })
          window.onmessage = function(e) {
            console.log(e.data); //I got your messgae, 3000!
          }
        </script>

    页面2(http://localhost:4000/2.html)

      window.onmessage = function(e) {
        console.log(e.data);// hello4000
        e.source.postMessage('I got your messgae, 3000!', 'http://localhost:3000');
      }

    4. window.name

    window.name是窗口的名称,存储的是一个字符串,不是字符串也会被自动转为字符串。

    只要浏览器窗口不关闭,该属性就存在;关闭后,属性消失。

    利用name属性的特性,可以通过修改iframe的src到同源的网址,获取跨域的窗口的name属性。

    因为iframe窗口一直存在,所以name属性一直存在;其他变量,重新加载就会消失;

    示例:

    任务:a,b同源; c是不同于a,b的源。从a页面获取iframe中c页面的name。
    原理:a页面加载完c页面后,虽然获取不到值,但是,相当于给c页面所在的iframe窗口设置了name属性;
    然后将src切换到b(同源空页面),此时a页面和所对应的iframe窗口同源,可以直接获取其name属性。

    代码:

    // c页面 http://localhost:4000/c.html
      <script>
        window.name = "hello"
      </script>
    // a页面 http://localhost:3000/a.html 
    <iframe id="iframeEle" name="ifr" 
      src="http://localhost:4000/c.html">
    </iframe>
    <script>
      let firstLoad = true;
      iframeEle.addEventListener('load', function() {
        if (firstLoad) { //c页面加载完成
          iframeEle.src = 'http://localhost:3000/b.html';
          // 修改后,iframe会重新load;
          firstLoad = false; //避免死循环
        } else {
          // 使用iframe的contentWindow属性,获取其window对象
          console.log(iframeEle.contentWindow.name); //hello
        }
      })
    </script>

    2. AJAX通信

    1. nginx

    架设服务器代理,浏览器通过请求同源服务器,再由该服务器请求外部服务。

    2. JSONP

    本质上已经不是AJAX请求,是js请求。

    特点: 只适用于GET方法。需要服务器端配合完成。

    浏览器通过动态添加script标签(不受同源策略限制),向服务器请求JSON数据。

    服务器收到请求后将数据放在指定名字的回调函数里返回回来。

    客户端代码(部署在3000端口):

    function jsonp({url, params, cb}) {
        return new Promise((resolve,reject) => {
          const script = document.createElement('script');
          // 全局函数
          window[cb] = function(data) {
            resolve(data);
            document.body.removeChild(script);//请求完成
          }
          let urlArr = [];
          params = {...params, cb};
          for(let key in params) {
            urlArr.push(`${key}=${params[key]}`)
          }
          script.src = `${url}?${urlArr.join('&')}`;
          document.body.appendChild(script); // 返回一个带参数的全局执行函数cb(data)
        })
      }
      jsonp({ 
        url: 'http://localhost:3001/query',
        params: {a: 'b'}, //传参
        cb: 'show' //指定回调函数名
      }).then(data => {
        console.log('data-->',data);  //{name: 'lyra'}
      })

    服务器代码:

    const express = require('express');
    const app = express();
    app.get('/query', (req, res) => {
        const { a, cb } = req.query;
        const data = {name: 'lyra'};
        res.end(`${cb}(${JSON.stringify(data)})`); //相当于返回一个立即执行函数show({name: 'lyra'})
    })
    app.listen(3001)

    3. WebSocket

    WebSocket是一种通信协议,使用ws://(非安全)和wss://(安全)作为协议前缀。

    特点:该协议不实行同源政策,只要服务器支持,就可以跨域通信。

    一般原生的不兼容;要通过socket.io库实现。

    客户端(部署在3000端口)示例:

        const ws = new WebSocket('ws://localhost:3002');
        ws.onopen = function() {
            ws.send('to server')
        }
        ws.onmessage = function(e) {
            console.log(e.data);
        }

    服务器端示例:

    const express = require('express');
    const app = express();
    const WebSocket = require('ws'); // npm install ws
    const socket = new WebSocket.Server({port: 3002}); //WebSocket单独一个端口
    socket.on('connection', function(ws) {
        ws.on('message', function(data) {
            ws.send('hello,I am server')
        })
    })
    app.get('/get', function(req,res) {
        res.end('end')
    })
    app.listen(3001)

    4. CORS(Cross-Origin Resource Sharing)

    Ajax请求的根本解决办法,是W3C标准。允许任何类型的请求。需要浏览器和服务器同时支持。

    主要是服务器支持。

    上面的三种办法都有局限:JSONP只支持GET,WebSocket只支持ws://协议。

    ➡️CORS详细信息

  • 相关阅读:
    js图片放大
    js编写点名器
    javascript中的math和随机数
    python中 __slots__
    python中 @property
    CentOS 6.5通过yum安装 MySQL-5.5
    linux下环境搭建
    oracle:ORA-01940无法删除当前已连接用户的解决方案
    不同版本apache(免安装)下部署Javaee多套项目
    使用poi处理excel
  • 原文地址:https://www.cnblogs.com/lyraLee/p/11858063.html
Copyright © 2020-2023  润新知