文章内容基于个人总结,如有问题欢迎评论。
1.何为跨域
关于网站的跨域问题,简单的说就是不同协议(如http|https)不同域名(如www.a.com|www.b.com|a.b.com)或者不同端口号(:3000|3001)之间的请求访问,都会产生同源限制导致请求失败。
2.解决方案
下面展示5种解决方案(包含代码及部分思路)
1) jsonp 根据script标签请求不会产生同源限制的原理 (局限在于只能发送get请求,及请求长度有限制)
客户端:
<div id="jsonp"></div> <script type="text/javascript"> function jsonpCallBack(data) { document.getElementById('jsonp').textContent = 'jsonp跨域callback-' + JSON.stringify(data); } </script> <script src="http://localhost:3000/api/jsonp?jsonp=jsonpCallBack"></script>
服务器端:
router.all('/jsonp', function(req, res, next) { var callback = req.query.jsonp; var obj = {name: 'tom', hobby: [1,2,3]}; res.send(callback ? callback + '(' + JSON.stringify(obj) + ')' : 'jsonp response'); });
思路: 在客户端先定义好callback方法,将方法作为url参数传到服务器端,服务器端返回方法调用并回传参数让客户端方法处理。
2) cors跨域 设置Access-Control-Allow-[Origin|Methods|Headers]等参数开启跨域许可
客户端:
<div id="cors"></div> <script type="text/javascript"> fetch('http://localhost:3000/api/cors', { method: 'get', mode: 'cors', cache: 'default' }) .then(res => res.json()) .then(data => { document.getElementById('cors').textContent = 'cors跨域-' + data; }); </script>
服务器端:
router.all('/cors', function(req, res, next) { res.header("Access-Control-Allow-Origin", "*"); // 最简设置 res.json('cors response'); });
3) iframe + window.name
客户端:
<div id="iframe"></div> <iframe src="http://localhost:3000" style="display: none" onload="iframeOnload()"></iframe> <script type="text/javascript"> document.domain = 'localhost'; function iframeOnload() { var iframe = document.getElementsByTagName('iframe')[0]; var win = iframe.contentWindow; win.fetch('http://localhost:3000/api').then(res => res.json().then(data => { document.getElementById('iframe').textContent = 'iframe跨域-' + data; })); }; </script>
服务器端:
router.all('/', function (req, res, next) { res.json('api response'); });
思路: 比较重要的一步是iframe页和请求页需要设置相同的domain, 即代码中执行的document.domain = 'localhost'这一段。
4) iframe + postMessage
客户端:
<div id="postMsg"></div> <iframe src="http://localhost:3000" style="display: none" onload="postOnload()"></iframe> <script type="text/javascript"> document.domain = 'localhost'; window.onmessage = function(e) { document.getElementById('postMsg').textContent = e.data; console.log('client window:' + e.data); } function postOnload() { var iframe = document.getElementsByTagName('iframe')[0]; var win = iframe.contentWindow; win.postMessage('test', '*'); // postMessage postMessage('postMsg response', '*'); } </script>
思路: 这里win.postMessage是给iframe发送消息,直接的postMessage是给自己发送消息。如果在iframe页面想给本页面发消息,则需要用window.parent.postMessage发送消息。
5) nginx反向代理
这个方式的实现就是修改下nginx配置,如
server { listen 3001; server_name localhost; location /api { proxy_pass http://127.0.0.1:3000/api; } }
通过这种设置就把本地3001端口的请求映射到3000端口去,然后就可以正常的发送之前同源限制的api接口了, 如客户端代码:
<div id="nginx"></div> <script type="text/javascript"> fetch('/api').then(res => res.json()).then(data => { nginx.textContent = 'nginx反向代理跨域-' + data; }) </script>
3. 总结
基本的几种方式就是这样,当然都是一些抛砖引玉的小实现思路,具体问题还是得具体分析的。然后,一些代码用到了像es6语法或者fetch api,有看不懂的。。还是补补课吧,毕竟还是比较通用流行的了。