非同源策略请求/WebSocket

2022-04-29  本文已影响0人  欢西西西

WebSocket使用及优化(心跳机制与断线重连)https://juejin.cn/post/6945057379834675230
vue项目使用WebSocket技术 https://juejin.cn/post/6982078455722557448

1.资源请求标签【link、img】-缺点:只能发起get请求,无法接收响应数据

        let img = new Image();

        img.src = '[http://192.168.4.154](http://192.168.4.154/):4005/list';

        let link = document.createElement('link');

        link.setAttribute("rel", "stylesheet");

        link.setAttribute('href', '[http://192.168.4.154](http://192.168.4.154/):4005');

        document.head.append(link); // 只设置href不会请求,必须添加才会请求

        document.head.removeChild(link);

2.JSONP【script标签】

要求服务端将返回数据和传过去的全局函数名称组织成可执行的js代码,缺点:只能发起get请求,随意执行响应数据,不安全

        window.execJson = function (dataJson) {
            console.log(dataJson);
        };

        let scripte = document.createElement('script');

        scripte.setAttribute('src', '[http://192.168.4.154](http://192.168.4.154/):4005/jsonp?funName=execJson');

        document.body.append(scripte);

        document.body.removeChild(scripte);

组织成可执行的js代码.png

3.iframe+form发送post请求-无法接收响应数据

 const requestPost = ({ url, data }) => {

            const iframe = document.createElement('iframe');

            iframe.name = 'iframePost';

            iframe.style.display = 'none';

            document.body.appendChild(iframe);

            const form = document.createElement('form');

            const node = document.createElement('input');

            iframe.addEventListener('load', function () {

                console.log('post success');

                document.body.removeChild(this);

            });

            form.action = url;

            // 在指定的iframe中执行form

            form.target = iframe.name;

            form.method = 'post';

            for (let name in data) {

                node.name = name;

                node.value = data[name].toString();

                form.appendChild(node.cloneNode());

            }

            // 表单元素需要添加到主文档中.

            form.style.display = 'none';

            document.body.appendChild(form);

            form.submit();

            // 表单提交后,就可以删除这个表单,不影响下次的数据发送.

            document.body.removeChild(form);

        }

        requestPost({

            url: '[http://192.168.4.154](http://192.168.4.154/):4005/postData',

            data: { name: 'wxm' }

        });

4.CORS 跨域资源共享-需要服务端设置允许某些访问源

image.png
server.get('/open', function (request, response) {

    console.log('get请求了open');

    // 允许哪些源访问,可以是通配符*

    response.setHeader("Access-Control-Allow-Origin", "[http://192.168.4.154](http://192.168.4.154/):4004");

    // 是否允许携带验证信息(非必须,如果请求携带了证书,需要设为true)    Credentials-证书

    response.setHeader("Access-Control-Allow-Credentials", true);
    response.setHeader("wxm-en", "ha");
    response.setHeader("hu-en", "haha");

    // 设置允许客户端访问的响应头
    response.setHeader("Access-Control-Expose-Headers", "wxm-en");

    response.send('open请求成功');
});
        fetch('[http://192.168.4.154](http://192.168.4.154/):4005/open', {
            // 默认情况下为omit:不发送;same-origin:同源情况下发送;include:都发送
            // 如果设置了发送,但服务端未设置Access-Control-Allow-Credentials或设为了false都无法请求
            credentials: 'include',
        }).then(res => {
            res.text().then((r) => {
                console.log(r);
            });
        });

Access-Control-Expose-Headers 仅设置了wxm-en没设置hu-en则访问不到hu-en :


image.png
fetch('[http://192.168.4.154](http://192.168.4.154/):4005/post/open', {

            method: 'post',
            headers: {
                "Content-type": "application/x-www-form-urlencoded",

                // 新增了一个头信息,此时该请求为非简单请求,会先发送一个预检请求,服务端也应处理这类预检请求
                "member-name": "wxm",
            },

            body: JSON.stringify({ length: 100 })
        }).then(res => {
            res.text().then((r) => {
                console.log(r);
            });
        })
preflight.png
OPTIONS.png
server.options('/post/open', function (request, response) {

    console.log('接收到post open的预检请求');

    response.setHeader("Access-Control-Allow-Origin", "[http://192.168.4.154](http://192.168.4.154/):4004");

    // 用于preflight request中,列出了允许的首部信息

    response.setHeader("Access-Control-Allow-Headers", "member-name");
    response.setHeader("Access-Control-Allow-Methods", "*");
    response.send('');
});

简单请求:①请求方式为GET/POST/HEAD ② 请求头不超过Accept、Accept-Language、Content-Language、Content-Type(只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)
预检请求:假设没有预检请求,跨域请求直接到达服务器,并有可能对数据库里的数据进行了操作,但是返回的结果被浏览器拦截了,那么我们就获取不到返回结果,这是一次失败的请求,但是可能对数据库里的数据产生了影响
// 浏览器必须先使用OPTIONS方法发起一个预检请求,从而获知服务器是否允许该跨域请求:如果允许,就发送带数据的真实请求;如果不允许,则阻止发送带数据的真实请求

5.postMessage -- 建立在页面基础上的跨域通信

image.png
 let iframe = document.getElementById('crossOrigin');  // 页面内嵌了一个非同源的页面
 iframe.contentWindow.postMessage("message info", "[http://192.168.4.154](http://192.168.4.154/):4006");

// 虽然能互相拿到引用但无法互相访问


image.png

// 但可以通过postMessage 进行通信:


onMessage.png
接收到通过postMessage发送的信息.png

6.WebSocket

不限制非同源连接,实现客户端与服务端的通信
服务端也可以将接收到的信息发给其他已连接的客户端,实现客户端与其他客户端的通信

        let wsUrl = 'ws://192.168.4.154:4007';
        let ws = new WebSocket(wsUrl);

        ws.onopen = function () {
            console.log('ws连接成功!');
        }
        ws.onmessage = function (event) {
            console.log(event.data);
        }
        window.onunload = function () {
            ws.close();
        }

        let msgInput = document.getElementById('messgae');
        document.getElementById('send').onclick = function () {
            ws.send(msgInput.value);   // 向服务端发送信息
        };

// WebSocket实例的属性

 {

        binaryType: "blob", // blob:Binary Large Object
        bufferedAmount: 0, // 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数
        extensions: "",
        onclose: null,
        onerror: null,
        onmessage: null,
        onopen: null,
        protocol: "",

        // 0未连接(WebSocket.CONNECTING)          
        // 1已连接(WebSocket.OPEN)             
        // 2正在关闭(WebSocket.CLOSING)               
        // 3已关闭(WebSocket.CLOSED) 
        readyState: 3,  

        url: "ws://192.168.4.154:4007/"
    }

// 心跳检测

var heartBeat = {
            interval: 1 * 1000,
            timeout: 3 * 1000, // 发出后timeout时间内还未收到信息则认为连接已断开

            wsUrl: 'ws://192.168.4.154:4007',
            ws: null,
            isConnecting: false,
            assertConnect: false,
            startHeartBeat() {
                setTimeout(() => {
                    if (this.ws.readyState === WebSocket.OPEN) {
                        console.log('<乒乒');
                        this.ws.send('wxm');
                        this.waitServer();
                    }

                }, this.interval);

            },

            waitServer() {
                this.assertConnect = false;
                setTimeout(() => {
                    if (this.assertConnect) { // 如果保持连接
                        this.startHeartBeat();
                    } else {
                        console.log('无响应,需重新连接...');
                        this.reConnect();
                    }
                }, this.timeout);
            },

            reConnect() {
                if (this.isConnecting) {
                    return;
                }

                try {
                    this.ws.close();
                    console.log('重新连接前先关闭连接...');

                } catch (err) {
                    console.log('连接已关闭,无需再次关闭');
                }

                this.init();
            },

            init() {
                console.log('连接中...');
                this.isConnecting = true;
                let ws = new WebSocket(this.wsUrl);
                this.ws = ws;



                ws.onopen = () => {
                    console.log('已连接√');
                    this.isConnecting = false;
                    this.startHeartBeat();
                };

                ws.onerror = () => {
                    console.log('连接失败');
                    this.isConnecting = false;
                    this.reConnect();
                };

                ws.onclose = () => {
                    this.isConnecting = false;
                    console.log('客户端:连接已关闭');
                };

                ws.onmessage = (e) => {
                    // 接收的如果是心跳消息

                    let isHeartMsg = true;

                    if (isHeartMsg) {
                        console.log('乓乓>');
                        this.assertConnect = true;

                    }

                };

            },

        }
上一篇下一篇

猜你喜欢

热点阅读