12

前言

常言道,"读万卷书,不如行万里路"。技术的学习也是如此,唯有实践才能更清楚的明白原理和加深印象,因此本文会利用node.js对前端的各种跨域方式进行实践,强烈建议一步一步跟着做,相信你肯定会对跨域有更深层次的理解。而由于篇幅限制,本文只会贴出关键性的代码,本系列总共分为上下篇。具体的代码请移步我的Github。如果对你有帮助的话,欢迎 star ヾ(´・ω・`)ノ

接上文--->「跨域」利用node.js实践前端各种跨域方式(上)

六、window.postMessage

postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:

1.页面和其打开的新窗口的数据传递
2.多窗口之间消息传递
3.页面与嵌套的iframe消息传递
4.上面三个场景的跨域数据传递

用法:postMessage(data,origin)方法接受两个参数
data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

来,我们来用node.js实践一下~

目录:postMessage/public/a.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>a</title>
    <iframe id="iframe" src="http://localhost:4444/b.html" style="display:none;"></iframe>
</head>
<body>
    <script>
        const iframe = document.getElementById('iframe');
        iframe.onload = function() {
            const data = {
                name : 'Jchermy'
            };

            //向http://localhost:4444 发送跨域数据
            iframe.contentWindow.postMessage(JSON.stringify(data), "http://localhost:4444");
        };

        //接受 localhost:4444 返回的数据
        window.addEventListener('message', function(e){
            alert('data from b.html ----->' + e.data);
        },false);
    </script>
</body>
</html>

在iframe加载完成后,向b.html所在域发送数据。因为postMessage是绑定在window对象上的,所以我们要获取iframe.contentWindow再发送数据,同时监听message,观察b.html有没有回传数据给我们。现在分别跑两个服务。访问localhost:3333可以看到:
图片描述

b.html接收到a.html发送过去的数据啦~

图片描述

然后a.html也收到了b.html回传的数据了。

跨域成功~ ( 。ớ ₃ờ)ھ

七、nginx 反向代理

这个方案的原理图如下:

图片描述

说明:当A域想与B域通信,可以通过nginx的反向代理,首先A域与同域的nginx服务器通信,然后nginx将请求转发到另外一个服务器,因为服务器之间的通信不存在跨域,这样我们就完成了跨域。具体实现如下:

首先,如果本地没有安装nginx的,需要安装nginx。安装完成后,我们对nginx进行配置:

目录:nginx-1.14.0/conf/nginx.conf

    server {
        listen       1111;
        server_name  localhost;

        location / {
            proxy_pass http://localhost:9999/; #反向代理到9999端口
            index  index.html index.htm;
            default_type "text/html";
            alias  "D:/Github/node-server/nginx/public/"; #client.html所在的本地的地址

            add_header Access_Control_Allow_Origin http://localhost:1111; 
            add_header Access_Control_Allow_Credentials true; #允许客户端带cookie访问
        }

然后,我们配置9999端口的服务器

目录:nginx/server.js

const http = require('http');
const server = http.createServer();
const qs = require('querystring');

server.on('request', function(req, res) {
    const query = require('url').parse(req.url, true).query;

    //向前台写cookie
    res.writeHead(200, {
        'Set-Cookie' : 'name=jchermy;Path:/;Domain:localhost;Httponly' //HttpOnly 脚本无法读取
    });
   
    res.write(JSON.stringify('Hi! '+query.user));
    res.end();
})

server.listen('9999');
console.log('Server is running at port 9999 .....');

这时候,我们打开浏览器,访问 http://localhost:1111/client.html?user=jchermy,看到下面的页面:

图片描述

我们在1111端口,将user=jchermy传给9999端口,然后9999端口接收到了我们的发送的信息并回传了"Hi! jchermy".说明这两个url可以跨域相互通信!完成~

七、node.js 中间件跨域

这个方案与nginx反向代理十分类似,只是将nginx代理服务器换成了node服务器。

目录:node-middleware/proxyserver.js

const express = require('express');
const proxy = require('http-proxy-middleware');
const app = express();

app.use('/login', proxy({
    //代理跨域的目标接口
    target: 'http://localhost:5555',
    changeOrigin: true,
    //修改响应头信息,实现跨域,并允许带cookie
    onProxyRes: function(proxyRes, req, res) {
        res.header('Access-Control-Allow-Origin', 'http://localhost');
        res.header('Access-Control-Allow-Credentials', 'true');
    },

    //修改响应信息中的cookie域名
    cookieDomainRewrite: 'http://localhost'
}));

app.use(express.static( './public'));
app.listen(3333);
console.log('proxy server is listen at port 3333');

目录:node-middleware/server.js

const http = require('http');
const server = new http.Server();
const qs = require('querystring');

server.on('request', function(request, response) {
    const query = require('url').parse(request.url, true).query;
    response.writeHead(200, {
        'Set-Cookie': 'name=amiee;Path:/;Domain:localhost:3333;Httponly'
    });

    response.write(`Hi, ${query.name} ! I come from localhost:5555`);
    response.end();
})

server.listen('5555');
console.log('Server is running at port 5555 .....')

最后,访问http://localhost:3333/login?name=hahah,可以看到:
图片描述

八、webSocket

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。
原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

首先,我们创建一个客户端的html页面。

目录:webSocket/public/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>index</title>
</head>
<body>
    <div>用户输入: <input type="text"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.js"></script>
    <script>
        window.onload = function(){
            var socket = io('http://localhost:5555');

            //连接成功处理
            socket.on('connect', function(){
                console.log('Server socket has established');
            });

            socket.on('disconnect', function() {
                console.log('Server sockect has closed');
            });

            //监听服务端消息
            socket.on('message', function(msg) {
                console.log('data from server ---->' +msg);
            } )

            document.getElementsByTagName('input')[0].onblur = function() {
                socket.send(this.value);
            };
        }
    </script>
</body>
</html>

将这个页面部署在3333端口上。

目录:webSocket/client.js

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

app.use(express.static('./public'));
app.listen(3333);
console.log('client is running at port 3333....');

最后,创建一个服务器,接收客户端的请求,并给予返回值

目录: webSocket/server.js

const http = require('http');
const socket = require('socket.io');
 
//启动http服务
const server = http.createServer(function(req, res) {
    res.writeHead(200, {
        'Content-type': 'text/html'
    });
    res.end();
})

server.listen(5555);
console.log('server is running at port 5555');

const io = socket(server);
//监听socket连接
io.on('connection', function (client) {
    //接收消息
    client.on('message', function (msg) {
        io.emit('message',  `hello, ${msg}`);
        console.log('data from client --->' + msg);
      });

      //断开处理
      client.on('disconnect', function() {
          console.log('Client socket has closed');
      });
});

将客户端和服务器端都跑起来,输入一些字符,当鼠标失去焦点后可以在服务器端5555端口的控制台看到:
图片描述

这说明服务器已经收到了客户端发送过去的字符。

此时在打开客户端页面的控制台,可以看到客户端也收到了服务器返回的字符:
图片描述

结语

这个系列终于写完啦,还是那句话,有错误和不合理的地方欢迎大家指正!如果文章对你有帮助的话,欢迎点赞收藏!!Github给个star就最好啦!=(//▽//)感谢大家~


Jchermy
857 声望132 粉丝

Keep learning