常见的跨域及其解决方法

跨域

在这里插入图片描述

1. jsonp

jsonp实现跨域的原理就是利用一些可以请求资源的标签,像scriptimg这类的标签它们请求资源是没有域的限制的。

实现

客户端

<script>
        function myFunction(data) {
            console.log(data);

        }
    </script>
    <script src="http://localhost:3000/test?callback=myFunction"></script>

服务器(返回的函数调用以字符串形式浏览器会自行解析)

const express = require('express')
const app = express()

app.get('/test', (req, res) => {
    const { callback } = req.query
    const data = {
        data: '这是服务中返回的数据'
    }
    res.send(callback + '(' + JSON.stringify(data) + ')')
})

app.listen(3000)
console.log('服务启动');

2. CORS

​ cors跨域请求又分为简单请求和非简单请求,而非简单请求是指对对服务器有特殊要求的这里不做演示,只需要知道非简单简单请求会在正式通信之前,增加一次HT TP查询请求,称为"预检"请求(preflight)

​ 对于一个简单请求,浏览器会直接发一个cors请求,即在请求的头部字段加一个源origin字段,写明此次请求来源。

​ 服务器则会根据信息,判断是否同意客户端的此次请求,其响应字段设置如下

  • Access-Control-Allow-Origin:必选,它的值要么是请求时Origin字段的值,要么是一个*,*表示允许全部。
  • Access-Control-Allow-Credentials :可选,值为boolean。表示是否允许发送cookie
  • Access-Control-Expose-Headers:可选

### 实现

客户端

<script>
        const xhr = new XMLHttpRequest();
        const url = 'http://localhost:3000/test'; // 请求的3000端口获取数据
        xhr.open('GET', url); // 与3000端口建立一个连接
        xhr.send(null);
        xhr.onload = () => {
            console.log(xhr.responseText);
        }
    </script>

服务器

const express = require('express')
const app = express()

app.get('/test', (req, res) => {
    res.set('Access-Control-Allow-Origin', 'http://127.0.0.1:5501');//允许源
    res.send('服务器消息')
})

app.listen(3000)
console.log('服务启动');

3. document.domain+iframe

​ 此跨域方案有很大的限制,它的应用场景是在主域相同,子域不同的情况下使用。

​ 原理:两端页面利用 document.domain设置成同一个基础主域

实现

a(父)页面

    <iframe src="http://b.domain.cn/b.html" id="iframe" style="display: none;" onload="show()"></iframe>
    <script>
        document.domain = 'domain.cn'; //设置成主域
        const data = 'data'
    </script>

b(子)页面

    <script>
       
        document.domain = 'domain.cn'; //设置成主域
        console.log(`来自a.html  ${window.parent.data}`);
    </script>

4. window.name+iframe

将传输的值放到window.name中

注意,直接去不同域页面上window.name的值是不被允许的,需要用一个同域页面做代理。

实现

3000端口中

a.html

 <iframe src="http://127.0.0.1:3001/b.html" id="iframe" style="display: none;"></iframe>
    <script>
        let count = 0;
        // onload事件会触发两次,第二次才是我们要的数据
        iframe.onload = function() {
            if (count == 0) {
                iframe.src = 'http://localhost:3000/proxy.html'; //先指同源
                count++;
                return;
            }
            console.log(iframe.contentWindow.name);

        }

proxy.html(空白即可仅作为中转)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <p>proxy</p>
</body>

</html>

3001端口中

b.html

<body>
    <script>
        //给a的数据在这里面
        window.name = 'gongxiaobai'
    </script>
</body>

5. location.hash+iframe

利用url中的hash进行传值,和上面6中一样。a与b通信同样需要借助一个中间人处理

实现

3000端口

a.html

    <iframe id="iframe" src="http://127.0.0.1:3001/b.html#aaaa" style="display:none;"></iframe>
    <script>
        function checkHash(res) {
            console.log(res);

        }
//传过来的值是放在hash上了,故监听hash值的变化
        window.onhashchange = function() {
            checkHash(location.hash.slice(1))
        }
    </script>

c.html(中间人)

  parent.parent.location.hash = self.location.hash.substring(1);

3001端口

b.html

var message = 'bbbbb'
        if (location.hash === '#aaaa') {
            var ifrproxy = document.createElement('iframe');
            ifrproxy.style.display = 'none';
            ifrproxy.src = 'http://localhost:3000/c.html#' + message; 
            document.body.appendChild(ifrproxy);
        }

6. postMessage+iframe

​ 这是一个h5的api,它同样也可以实现跨域。既然是一个api,那么一般就有参数。 postMessage(data,origin)

​ 这个api方法有两个参数,1为传输的数据(基本上任意类型)最好转成json字符串毕竟跨域对面的语言不知是啥,2为源(协议+主机+端口)可为*、同源设为/

实现

3000端口

a.html

  <iframe id="iframe" src="http://127.0.0.1:3001/b.html" style="display:none;"></iframe>
    <script>
        iframe.onload = function() {
            const data = {
                name: 'a的信息'
            };
            // 向b传送跨域数据
            iframe.contentWindow.postMessage(JSON.stringify(data), 'http://127.0.0.1:3001');
        };
    </script>

3001端口

b.html

    <script>
        // 接收a的数据
        window.addEventListener('message', function(e) {
            console.log(e.data); //a的数据
        }, false);
    </script>

7. WebSocket

WebSocket protocol 同样来自h5,它是h5的一个新协议。它是全双工通信喔,不过使用WebSocket 的API不方便故一般均会使用Socket.io,Socket.io对于WebSocket 有很好的封装。

实现

客户端

<script>
        var io = io.connect('http://127.0.0.1:3000');
        io.on('serive', function(data) {
            console.log(data);

        });
</script>

服务器

const app = require('express')();
const server = require('http').createServer();
const io = require('socket.io')(server);

io.on('connection', function(client) {
    client.emit('serive', 'aaaa');
});

server.listen(3000)
console.log('ok');

9. 各种代理

使用这个实现的原理:服务器与服务器之间是没有跨域问题的,故可以用中间的一个代理向目标浏览器发送请求

代理有以下几种方式,这里不做演示了

  • http-proxy-middleware

  • 打包工具中

  • nginx

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值