《跨域请求的十种解决方案》

写在前面:

最近有个词一直在耳边,那就是跨域请求。
个人觉得跨域请求还是比较容易解释明白的,同时也能扩散的了解一些其他知识。
比如域的概念,同源策略,如何解决跨域,前端安全,http代理等。
本篇文章会从常见的跨域请求开始发散,通过demo的形式对相关的知识点进行介绍。
下面开始。

目录:

什么是域?

域,本义指范围:领域,地域,区域。
每个网站都有自己的域名,例如:www.baidu.com。
主域名可以解析出二级域名,例如:tongji.baidu.com
二级域名与主域名就是两个单独的域。每个域都是单独的领域空间.
不同域之间不可以互相操作Dom,发送请求获取数据,以及互相操作Cookie。
所以很多项目都把自己的前端和后台项目部署到同一域下。
但是随着项目的发展,你需要对静态资源做合理的优化,需要对后台接口服务化,来实现较高的复用性。
这样的场景下,就需要在A域下引用B域的js,img,css等静态资源,以及在A域下调用B域的接口获取数据。
相信你绝对遇到过这样的现象:跨域引用静态文件时没有一点问题,但是跨域发送请求时却总是异常
为什么呢?下面我们就来分析这种现象,首先要讲的就是浏览器的同源策略(Same-origin policy)

同源策略(Same-origin policy)

同源策略是web应用的一个重要的安全策略。它也是web浏览器最核心最基本的安全策略。
同源策略可以防止恶意脚本访问另一网页的敏感数据。
试想下,如果没有同源策略的保护,只要用户访问恶意网站A,网站A内的恶意脚本就可以获取其他已经打开的页面数据,
包括cookie中的信息,会话Token,密码输入框的内容等。
有了这些信息,就可以伪造用户的请求,执行恶意的转账,发布信息等操作。
当然同源策略并不能避免所有的攻击,但很大限度的增加了攻击的复杂度。
目前为止所有支持javascript的浏览器都会使用这个安全策略。同源策略允许来自相同来源的网页之间访问数据。
什么是相同来源呢?相同来源是指域名,协议,端口全部相同的网页。具体看下表:

以上就是同源策略允许的数据交互,可以看出同源策略是很严格的,即使主域名与二级域名之间也不允许互相操作数据。
有的同学就要问了,在www.a.com下引用www.b.com/common.js
在www.a.com下ajax请求www.b.com下的接口这些都是常见的现象,
既然不允许为什么还有这么多人在这么做呢?接下来我们就来深入的了解下跨源网络请求。

跨域请求

跨域请求也叫跨源请求,指的是跨域页面之间存在XMLHttpRequest, <img>, <script>, <link>之类的跨域引用或跨域请求。
这类交互的实现根据浏览器的不同会存在一些差异,通常分为三大类:

(1).允许跨域写操作(Cross-origin writes)

例如链接(links),重定向以及表单提交。由A页面点击a标签跳转到B页面,那么浏览器允许B页面数据覆盖A页面原有数据。重定向以及表单提交都会有这样的效果。

(2).不允许跨域读操作(Cross-origin reads)

但可以通过内嵌资源来巧妙的进行读取访问。例如可以读取嵌入图片的高度和宽度,调用内嵌脚本的方法。A源下不能获取B源下的任何信息。可以将B源下的资源嵌入当前源,来进行读取。

(3).允许跨域资源源嵌入(Cross-origin embedding)

跨域内嵌资源的示例:
1.<script>标签嵌入跨域js脚本。当前域的语法错误信息只能在同域脚本中捕捉到,在嵌入的跨域脚本中是获取不到的。
2.<link>标签嵌入跨域css文件。
3.<img>标签嵌入跨域图片文件。
4.<video> 和 <audio>嵌入多媒体资源。
5.<object>, <embed> 和 <applet>等插件的嵌入。
6.@font-face引入的字体。视浏览器而定,一些浏览器允许跨域字体,一些只允许同源字体。
7.<frame> 和 <iframe>载入的任何资源。站点可以使用X-Frame-Options消息头来阻止这种形式的跨域交互。

以上的介绍来自mozila的文档说明:javascript的同源策略
通过前面三点说明,我们可以得出结论:跨域的js,css,img等文件的引用属于跨域内嵌引用,是允许的。
跨域内嵌引用方便了我们开发web应用,增加便捷性的同时也带来了一些安全问题。
首先跨域的js脚本引用是允许的,所以只需要将攻击脚本想办法添加到被攻击页面。那么就可以执行攻击代码。
这个其实不太容易做到,但不是做不到。比如:DNS(污染)劫持,ISP劫持,会话劫持等。
运营商或者第三方通过劫持正常的用户请求。当我们访问www.baidu.com
此次请求被运营商拦截,对返回结果进行包装处理:

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <script type="text/javascript" src="http://www.other.com/js/assault.js"></script>
</head>
<body >
    <embed src="http://www.baidu.com" />
</body>
</html>

通过这样的方式将广告的脚本引用到页面中来。不细心观察根本发现不了。
如果网络提供商的服务被攻击挂马,这样的攻击往往防不胜防,
还有一些比较常见的,例如xss注入,通过提交一些包含script标签的评论,来达到引用攻击脚本的目的。
有人该说了,引用了js文件,又有什么用呢?
通过引用攻击脚本可以获取你的cookie,恶意代码模拟你来提交数据,或是将你的数据发送到攻击者的服务器,盗取数据。
获取了cookie也发送不出去啊,前面我们讲过是不允许跨域发送数据的吗?
所以,我们了解了跨域引用脚本等静态文件的原理之后,我们再来看看如何解决跨域发送请求的问题。

跨域请求的解决方案

介绍了一些跨域资源的交互方式之后,介绍一下最常见的跨域请求。
前面我们讲的同源策略里指出是不允许跨域发送请求的,那面临这样的场景时需要如何处理呢?
在正式介绍之前,我们先准备一个跨域demo。
服务端:

const express    = require('express');
const app        = express();
const bodyParser = require('body-parser');

app.use(bodyParser.urlencoded({ extended: false }));

app.post('/logStash/test', bodyParser.json(), (req, res) => {
    res.status(200).json({ isOk:true, msg:"success" });
});

app.listen(8061,() => {
    console.log('服务已启动,正在监听:8061端口。');
});
//在8061端口暴露一个接口: http://localhost:8061/logStash/test

客户端:

<!DOCTYPE html>
<html>
<head>
	<title>跨域请求</title>
</head>
<body>
	跨域请求demo
</body>
<script type="text/javascript">
  window.onload = function(){
    var url = 'http://localhost:8061/logStash/test';
    var xhr = createxmlHttpRequest();
    xhr.responseType = "json";
    xhr.open("post", url, "true");
    xhr.setRequestHeader("Content-Type","application/json;charset=UTF-8;");
    xhr.send();
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            if(xhr.status == 200){
                var data = xhr.response;
                window.document.write(data.msg);
            }
        }
    };

    function createxmlHttpRequest() {
        if (window.ActiveXObject) {
            return new ActiveXObject("Microsoft.XMLHTTP");
        } else if (window.XMLHttpRequest) {
            return new XMLHttpRequest();
        }
    }
</script>
</html>
//我们将此页面部署到:http://localhost:8080/index.html

以上就是我们做的跨域demo,看下效果:

这样的请求是不被允许的,下面我们开始介绍几种解决方案。

方案一: 《本地跨域开发》

当有人问我如何解决跨域问题时,我首先会问跨域场景是在开发阶段存在,还是上线后也会存在。
举一个例子,我们接到一个页面优化的需求。当你在localhost下进行开发,去调用生产的域名接口,就会发生跨域问题。
但是你的页面上线后就是在同域下,就不会存在跨域问题。这样的场景该如何解决跨域问题呢?
同源策略是浏览器层的约束,你可以直接在浏览器中设置关闭同源策略。
这只适用于开发阶段,因为你不能要求你的所有用户都关闭同源策略。
所以这个解决方案不会涉及到后台以及前端代码,只需要你修改一下你的开发浏览器即可。
不同的浏览器有不同的关闭方式。可以搜索关键词:”浏览器名“ + ”跨域请求“
IE浏览器下,进入Internet选项 ——> 选择安全性 ——> 选择internet ——> 选择自定等级 ——>
在弹出的窗口中找到:跨域浏览器窗口和框架 选择允许 ——> 找到:通过域访问数据源 选择允许
设置之后便可以使用该浏览器发送跨域请求。其他浏览器都可以找到对应的设置方法。
chrome浏览器下的解决方案比较简单,可以直接执行一条命令来打开允许跨域的chrome浏览器:

$ open -n /Applications/Google\ Chrome\ 2.app --args --disable-web-security --user-data-dir=用户数据目录

这是本地开发比较简单的一种解决跨域问题的办法。我们只需要更改自己本地浏览器的设置即可。

方案二: 《跨域资源共享CORS》

方案一如果解决不了眼前的问题的话,我会问第二个问题,你能不能修改后台代码,或者说能不能让后台开发做一些修改?
因为方案二需要接口端修改,不需要前端做改动。
CORS是一个W3C标准,全称”跨域资源共享“(Cross-origin resource sharing)
是在服务端在响应头中增加一些字段,用来告诉浏览器服务端允许来自哪些域的请求,允许哪些类型的请求,具体字段如下:

(1)Access-Control-Allow-Origin

用来告诉浏览器服务端接收哪些域的请求,值可以是一个具体的域名(www.baidu.com),也可以是通配符(*)

(2)Access-Control-Allow-Credentials

用来标识服务端是否允许发送Cookie,需要搭配XMLHttpRequest的withCredentials一起使用。
如果设置为发送Cookie时,Access-Control-Allow-Origin必须制定具体域名,而不能是通配符(*)

(3)Access-Control-Expose-Headers

用来标识允许获取的返回头内容

(4)Access-Control-Allow-Methods

该值必需,用来告诉浏览器服务端支持的所有请求类型

(5)Access-Control-Allow-Headers

如果是复杂请求,该值必需。用来表明服务器支持的所有头信息

(6)Access-Control-Max-Age

标识复杂请求下预检请求的有效期。

我们对服务端做下修改:

const express    = require('express');
const app        = express();
const bodyParser = require('body-parser');

app.use(bodyParser.urlencoded({ extended: false }));

app.all('/logStash/test', (req, res, next) => {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Authorization, Accept, X-Requested-With");
    res.header("Access-Control-Allow-Methods", "POST");    
    next();
});

app.post('/logStash/test', bodyParser.json(), (req, res) => {
    res.status(200).json({ isOk:true, msg:"success" });
});

app.listen(8061,() => {
    console.log('服务已启动,正在监听:8061端口。');
});

再次尝试发送跨域请求:

请求可以发送出去,但是奇怪的是控制台中显示有两个请求:

一个OPTIONS请求,一个POST请求。这是为什么呢?
我们把所有的请求分为两大类,一类叫做简单请求,一类叫做非简单请求
非简单请求是一类对服务器有特殊要求的请求,比如PUT,DELETE请求,或者是Content-Type值为application/json的请求。
除此以外的请求都是简单请求。
简单请求与非简单请求最大的区别是后者会在正式请求之前增加一次查询,称之为预检请求(preflight)
预检请求的请求类型是OPTIONS,表示这个请求是用来询问的。
预检请求主要作用是告知浏览器服务端的跨域请求设置,就是我们之前讲到的Access-Control-Allow-Origin等配置项。
浏览器根据预检请求的响应头来判断是否发出正式请求。下面我们看一下简单请求的样子,
只需要将客户端请求的Content-Type设置为text/plain即可:

xhr.setRequestHeader("Content-Type","text/plain;charset=UTF-8;");

效果:

这就是为什么有的跨域请求是一次请求,有的是两次请求。
是因为非简单请求在正式发送之前,会有一次OPTIONS类型的预检请求。
以上就是利用CORS来解决跨域请求的介绍,是所有解决方案中比较简单的,只需要在后台增加一个全局的拦截器即可处理。
但有很多场景后台接口修改不是很方便,所以我们接着介绍其他方案。

方案三: 《nginx代理跨域》

如果说你觉得每次需要更改浏览器设置太麻烦了,后台又不能配合修改。下面介绍第三种跨域方案,nginx代理。
同源策略是浏览器的安全策略,不是HTTP协议的要求。所以只需要将请求的发送方交由服务端发送就可以绕过同源策略了。
nginx代理跨域就是利用这一点实现。
nginx安装过程就不多说,安装好之后需要修改下nginx的配置,找到nginx.conf文件做如下修改:

server {
    listen       90;
    server_name  localhost;

    location / {
        proxy_pass http://localhost:8061;
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Headers Content-Type;
	add_header Access-Control-Allow-Methods POST;
    }
}

我们在之前客户端直接请求服务端的模型中加入了nginx,这套模型的交互就变成了:客户端请求nginx,nginx请求服务端。
在这个过程中nginx请求服务端是不存在跨域的,因为它是服务端请求服务端,不涉及到同源策略。
但是客户端请求nginx的过程还是受到同源策略的限制的,所以我们要在nginx的配置中加入对应的响应头,以便浏览器能正确请求到nginx。
整个nginx配置的意思大致为:
我们将nginx服务端部署到90端口,并将所有发送到localhost:90的请求全部转发到localhost:8061,这一步就是nginx请求服务端.
然后我们将客户端的代码也做下修改,将之前发送到localhost:8061的请求更改为localhost:90,也就是客户端直接请求nginx:

window.onload = function(){
    //var url = 'http://localhost:8061/logStash/test';
    var url = 'http://localhost:90/logStash/test';
    var xhr = createxmlHttpRequest();
    xhr.responseType = "json";
    xhr.open("post", url, "true");
    ...
};

服务端的代码将我们方案二添加的响应头去掉,保持最一开始的样子:

const express    = require('express');
const app        = express();
const bodyParser = require('body-parser');

app.use(bodyParser.urlencoded({ extended: false }));

app.post('/logStash/test', bodyParser.json(), (req, res) => {
    res.status(200).json({ isOk:true, msg:"success" });
});

app.listen(8061,() => {
    console.log('服务已启动,正在监听:8061端口。');
});

看下效果:

和之前一样,非简单请求会多一次预检请求。唯一的变化就是由请求8061变为了请求90.
当我们在本地开发需要做跨域时,可以在你和后台之间加一层nginx来做转发,包括在生产环境也可以这样做。
介绍完了nginx,我们顺便再看一种和nginx极其类似的方案。

方案四: 《node proxy middleware代理跨域》

node提供了丰富的中间件来方便我们的开发工作,今天我们介绍其中一个:http-proxy-middleware
我们之前在《传统项目实现前后端分离开发》这篇文章中讲了一个场景,让我从nginx过渡到了http-proxy-middleware
主要原因是我们团队的小伙伴认为nginx较为复杂,并且需要单独管理,不方便,所以我想把nginx的功能合并到我们的node开发工具中。
与方案三的nginx类似,只是用一个node app来替代nginx的功能,看代码:

//proxy.js
const express  = require('express');
const app 	   = express();
const proxyMiddleware = require('http-proxy-middleware');

//proxy
app.use('/', proxyMiddleware({
    secure: true,
    target: "localhost:8061",
    changeOrigin: true,
    onProxyRes: function(proxyRes, req, res) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Authorization, Accept, X-Requested-With");
     res.header("Access-Control-Allow-Methods", "POST");
    }
}));

app.listen(9061,() => {
    console.log(' proxy 代理服务器已启动,正在监听:9061端口。');
});

新增的proxy.js主要作用是将所有的请求代理到8061端口的服务端。
客户端也做简单的修改:

window.onload = function(){
    //var url = 'http://localhost:8061/logStash/test';
    var url = 'http://localhost:90/logStash/test';
    var xhr = createxmlHttpRequest();
    xhr.responseType = "json";
    xhr.open("post", url, "true");
    ...
};

原理和nginx一样,这里我们就不赘述了。接着往下介绍。

方案五: 《JSONP跨域》

自从13年刚实习时使用JSONP以后没有再见过,所以放到后面点来讲,如果前面的方案解决不了问题的话,再选择向后继续了解。
了解JSONP之前,先提一下JSON.
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。JSON其实就是JavaScript中的字面量对象。
JSONP(JSON with Padding)是一个非官方的协议。
我们前面在讲同源策略的时候讲过,不同域之间允许跨域资源源嵌入(Cross-origin embedding)
也就是可以跨域引用js,css,img文件等资源,
那么问题来了,如果script标签的src指向的不是一个静态资源,而是一个接口地址可以请求成功吗?
我们试一下:
修改客户端代码,增加一个script标签,src是后台接口地址

<!DOCTYPE html>
<html>
<head>
	<title>跨域请求</title>
</head>
<body>
	跨域请求demo
</body>

<script type="text/javascript" src="http://localhost:8061/logStash/test"></script>
</html>

因为script标签发起的是get请求,所以我们需要修改服务端代码,以支持get请求:

//app.post('/logStash/test', bodyParser.json(), (req, res) => {
app.get('/logStash/test', bodyParser.json(), (req, res) => {
    res.status(200).json({ isOk:true, msg:"success" });
});

看下结果:

正常返回,没有跨域警告,但是问题来了,我们怎么拿到返回值呢?
能不能在返回结果中执行某个方法呢?
修改客户端代码,加入一个函数:

<body>
	跨域请求demo
</body>
<script type="text/javascript">
    function setBody(data){
        window.document.write(data);
    }
</script>
<script type="text/javascript" src="http://localhost:8061/logStash/test"></script>

再修改服务端的返回内容,不返回json,而是返回一段代码:

app.get('/logStash/test', bodyParser.json(), (req, res) => {
    // res.status(200).json({ isOk:true, msg:"success" });
    res.send("setBody('jsonp demo')");
});

试一下:

正如预期的结果一样,返回的代码片段被顺利的执行。我们接着思考,如何动态化setBody这个执行函数呢?
我们可以试着通过参数来传递函数名。
修改客户端代码:

<script type="text/javascript" src="http://localhost:8061/logStash/test?callback=setBody"></script>

修改服务端:

app.get('/logStash/test', bodyParser.json(), (req, res) => {
    // res.status(200).json({ isOk:true, msg:"success" });
    res.send(req.query.callback + "('jsonp demo')");
});

看下结果:

上面的几次修改,我们就用原生的方法实现了一个jsonp传输方式,ajax将我们前面的步骤进行封装,成了下面的样子:

$.ajax({
    url : "http://localhost:8061/logStash/test",
    type : "GET",
    dataType : "jsonp", 
    jsonp : 'callback',
    jsonpCallback: 'handleResponse', //设置回调函数名
    success: function(response, status, xhr){
        console.log(response);
    }
});

这次请求的最终展现:http://localhost:8061/logStash/test?callback=handleResponse
得到的返回值也就是:handleResponse(‘jsonp demo’)
原理和我们自己实现的是一样的。
jsonp也有自己的缺点,它只能以get请求的方式发起。并且如果动态脚本执行过程中发生异常,是无法监听到的。
H5对script标签增加了onerror的异常监听,但是少有浏览器实现。
所以,jsonp的解决方案作为备选,优先选择前面的解决方案。
接口请求的跨域解决介绍到这里,接下来我们介绍几种iframe的跨域解决方案。

方案六: 《document.domain解决iframe跨域》

我们常常使用iframe来打开一个窗口,在这种场景下,窗口和父级页面之间也存在域的限制。
我们接下来重点解决这一类跨域问题。
此方案适用的范围比较小,只能解决主域相同,子域不同的跨域数据传输。
解决的办法也比较简单。
父级页面:www.baidu.com

<iframe src="http://play.baidu.com/index.html"></iframe>
<script>
    document.domain = 'baidu.com';
    var name = 'xiaoqiang';
</script>

iframe子级页面:play.baidu.com/index.html

<script>
    document.domain = 'baidu.com';
    alert('username:' + window.parent.name); //获取父级页面数据
</script>

这个只能作为一个小知识点,因为很小概率会遇到这样的场景,局限性太高了。

方案七: 《window.name解决iframe跨域》

除了利用domain的特性外,还可以利用一个特殊的属性: window.name
同一个iframe在打开不同的页面后,甚至不同域名的页面后,window.name是保持不变的,并且大小支持2M的长度。
看例子:
demo1,创建一个iframe,指向demo2的页面

<!DOCTYPE html>
<html>
<head>
	<title>跨域请求</title>
</head>
<body>
	demo1
</body>
<script type="text/javascript">
	var loadData = false,
		iframe = document.createElement('iframe');

	  	iframe.src = "http://localhost:9061/demo2.html";
	    iframe.onload = function() {
	    	if(!loadData){
	    		loadData = true;
	    		iframe.contentWindow.location = "http://localhost:8080/demo3.html";
	    	}else{
	    		alert(iframe.contentWindow.name);
	    	}
	    };
	    document.body.appendChild(iframe);
</script>

</html>

demo2的页面比较简单,只是给window.name做赋值。

<!DOCTYPE html>
<html>
<head>
	<title>跨域请求</title>
</head>
<body>
	demo2
</body>
<script type="text/javascript">
    window.name = 'demo 2';
</script>
</html>

demo3与demo1同域,只是一个空白页面:

<!DOCTYPE html>
<html>
<head>
	<title>跨域请求</title>
</head>
<body>
	demo3
</body>
</html>

最终效果:

解释一下:
其实一开始我也不是很理解,为什么还要再跳一个空白页面demo3才能实现,但其实重点就在demo3
首先demo1打开demo2,demo2给window.name赋值。
这个时候在demo1中是不能直接获取window.name的值,会提示不能跨域读取。
所以我们打开一个同域的demo3,同域之间是可以直接读取的。
正是因为window.name在iframe地址变化的过程中一直保持不变,利用这个特性便可以在iframe中跨域传递数据。

方案八: 《location.hash解决iframe跨域》

理解了前面的window.name再来看location.hash方案就比较容易理解了,
前面的demo1和demo2不能直接通信,加入了demo3来做桥梁,方案七和方案六的原理是一样的。
修改下代码,
demo1中增加iframe指向demo2,增加callback函数来接收数据:

<iframe src="http://localhost:9061/demo2.html" style="display:none;"></iframe>
<script>
  function Callback(res) {
    alert(res);
  }
</script>

demo2中增加iframe指向与demo1同域的demo3,用定时器发送数据给demo3

<iframe id="iframe" src="http://localhost:8080/index3.html" style="display:none;"></iframe>
<script>
  	var iframe = document.getElementById('iframe');
  	 setTimeout(function() {
	    iframe.src = iframe.src + '#data=demo2';
	  }, 1000);
</script>

demo3与demo1同域,可以执行callback来传递参数

<script>
  // 监听demo2传来的hash值
  window.onhashchange = function () {
    // 通过window.parent找到同域的demo1页面,调用其Callback函数传递参数
    window.parent.parent.Callback(location.hash.replace('#data=', ''));
  };
</script>

这个方案,我觉得能用到的场景更是少得可怜,原理也不是很复杂,比较好理解,我们也不多做介绍了。

方案九: 《postMessage解决iframe跨域》

接下来我们介绍一个html5的新特性,postMessage
通过postMessage可以解决多窗口之间的消息传递,也可以在页面与嵌套iframe之间传递消息。功能比较强大
postMessage(data, origin)接受两个参数,一个是需要传递的数据data,一个是接受域origin。
看例子:
demo1

<script>
    iframe = document.createElement('iframe');
    iframe.src = "http://localhost:9061/index.html";
    iframe.onload = function() {
        //iframe.contentWindow.postMessage("{'data':'demo1'}", '*');//任意域可接收
        iframe.contentWindow.postMessage("{'data':'hello demo2'}", 'http://localhost:9061');
    };
    document.body.appendChild(iframe);
    //接收消息
    window.addEventListener('message', function(e) {
        console.log(e.data);
    }, false);
</script>

demo2:

<script>
    // 接收消息
    window.addEventListener('message', function(e) {
        window.document.write('收到消息:'+e.data);
        window.parent.postMessage("{'data':'hello demo1'}", 'http://localhost:8080');
    }, false);
</script>

效果:

解释一下,消息发送发通过postMessage向指定的域发送消息,域收到消息后,将数据交给域下的各个message事件监听的回调函数处理。
不会有跨域问题,也是比较好的解决方案,但是在使用新特性时需要注意兼容性问题。

方案十: 《WebSocket协议跨域》

WebSocket是HTML5支持的一种新协议,它支持客户端与服务端的双向通信,并且允许跨域通信。
我们使用Socket.io来做一个WebSocket跨域通信的demo:
客户端:

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js"></script>
<script>
 	var socket = io.connect('http://localhost:9061');
	socket.on('clientEvent', function (data) {
	    console.log(data);
	    socket.emit('serverEvent', { my: 'demo1' });
	});
</script>

服务端:

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

io.on('connection', function (socket) {
  socket.emit('clientEvent', { hello: 'demo1' });
  socket.on('serverEvent', function (data) {
    console.log(data);
  });
});


server.listen(9061,() => {
	console.log(' socket服务器已启动,正在监听:9061端口。');
});

效果:

通过WebSocket来进行双向通信,其天生允许跨域的特性能解决特定场景下的跨域问题。同样在使用它之前,考虑好设备兼容。

总结:

至此,js以点破面 -《跨域请求》这篇文章想要说的点也差不多了,例子比价多,感觉有点乱。
跨域问题是很多人都会遇到的问题,不论是做前端还是后台。
这篇文章也是来自于一些朋友同事遇到的问题,既然有人需要,就抽时间整理分享出来。
既是对自己基础知识的巩固,也能培养下整理知识点的能力。
最后,有任何问题建议都可以添加我的微信或者发邮件来沟通。谢谢