文章目录
- 前言
- 跨域解决的方法
- 1.JSONP
- 2.CORS跨域资源共享
- 3.http proxy => webpack webpack-dev-server
- 4.nginx反向代理
- 5.postMessage(跟Worker很像)
- 6.WebSocket协议跨域
- 总结
前言
为了加快请求响应时间,服务器进行分布式布局,将服务器分为:
web服务器:用于处理静态资源
data服务器:业务逻辑和数据分析
图片服务器
由于浏览器的同源策略,同源策略认为以下三者都一样就是同源,只要有一个不同就是跨域:
- 协议
- 域名
- 端口号
导致需要用到跨域请求。
跨域解决的方法
1.JSONP
使用不存在跨域请求的限制的html标签发送get请求,比如以下标签:
-script
-img
-link
-iframe
jsonp实现:
服务器:
let express = require('express'),
app = express();
app.listen(8001, () => {
console.log("OK!");
})
app.get('/list', (req, res) => {
let { callback = Function.prototype } = req.query;
let data = {
code: 0,
message: "珠峰培训"
};
res.send(`${callback}(${JSON.stringify(data)})`)
})
客户端:
<!DOCTYPE html>
<html>
<head>
<title>GoJSONP</title>
</head>
<body>
<script type="text/javascript">
function jsonhandle(data){
alert("age:" + data.age + "name:" + data.name);
}
</script>
<script crossorigin="anonymous" integrity="sha384-rY/jv8mMhqDabXSo+UCggqKtdmBfd3qC2/KvyTDNQ6PcUJXaxK1tMepoQda4g5vB" src="https://lib.baomitu.com/jquery/2.2.4/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
var url = "http://127.0.0.1:8001/list?callback=jsonhandle";
var obj = $('<script><\/script>');
obj.attr("src",url);
$("body").append(obj);
});
</script>
</body>
</html>
结果:
问题:JSONP只能支持GET请求。
2.CORS跨域资源共享
做法:
1.客户端(发送ajax/fetch请求)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src = "./node_modules/_axios@0.21.1@axios/dist/axios.js"></script>
<script>
axios.get('http://127.0.0.1:3001/list')
.then(result =>{
console.log(result);
})
</script>
</body>
</html>
2.服务器端设置相关的头信息(需要处理options试探性请求)
let express = require('express'),
app = express();
app.use('*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*'); //这个表示任意域名都可以访问,这样写不能携带cookie了。为了保证安全
//res.header('Access-Control-Allow-Origin', 'http://www.baidu.com'); //这样写,只有www.baidu.com 可以访问。
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');//设置方法
if (req.method == 'OPTIONS') {
res.send(200); // 意思是,在正常的请求之前,会发送一个验证,是否可以请求。
}
else {
next();
}
});
app.listen(3001, () => {
console.log("OK!");
})
app.get('/list', (req, res) => {
let data = {
code: 200,
message: "我是服务器的数据!"
};
res.send(data)
})
3.http proxy => webpack webpack-dev-server
我们配置如下:
webpack.config.js
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: "production",
entry: './fun.js',
output: { // 输出配置
filename: './built.js', // 输出文件名
path: resolve(__dirname, 'build/js') // 输出文件路径配置
},
plugins: [
new HtmlWebpackPlugin({
template: './please.html',
filename: 'index.html'
})
],
devServer: {
port: 3000,
progress: true,
contentBase: './build',
open: true
// proxy: {
// '/': {
// target: 'https://www.baidu.com/',
// changeOrigin: true
// }
// }
}
}
fun.js如下:
import axios from "axios"
axios.get('https://www.baidu.com/').then(res => {
console.log(res);
})
打开webpack内置的服务器: npx webpack-dev-server。
会发生跨域,那么我们需要把devServer上的proxy注释去掉。就可以了。
这是Node中间件 代理实现的!
就是用Node来搭了一个代理服务器帮忙转发客户端的请求??毕竟服务器和服务器不存在跨域。
如果要发行呢??毕竟devServer 是方便我们调试。那么就需要用node来做代理了。
4.nginx反向代理
不需要前端干啥。其实就是nginx帮我们加了Access-Control-Allow-Origin的请求头啦。
5.postMessage(跟Worker很像)
我们先看看postMessage由谁来调用:
语法
otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow
其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。
另外一种说法是:
otherWindow
对将接收消息的窗口的引用。获得此类引用的方法包括:
-
Window.open
(生成一个新窗口然后引用它), -
Window.opener
(引用产生这个的窗口,返回打开当前窗口的那个窗口的引用,例如:在window A中打开了window B,B.opener 返回 A.), -
HTMLIFrameElement.contentWindow
(<iframe>
从其父窗口引用嵌入式), -
Window.parent
(从嵌入式内部引用父窗口<iframe>
) -
Window.frames
+索引值(命名或数字)。
或者看看MDN上描述:
window.postMessage MDN
otherWindow.postMessage(message, targetOrigin, [transfer])
otherWindow: A reference to another window(发送方的引用)
message:Data to be sent to the other window.(要发送到接受方的数据)
targetOrigin:Specifies what the origin of otherWindow must be for the event to be dispatched(接收方的 源,还有必须要有监听message事件)
使用:
我们先建立两个不同源的服务器:
let express = require('express'),
app = express();
app.use(express.static('./'));
app.listen(1001, () => {
console.log("port of 1001 OK!");
})
let express = require('express'),
app = express();
app.use(express.static('./'));
app.listen(1002, () => {
console.log("port of 1002 OK!");
})
1001.html,这个文件跑在域名为127.0.0.1:1001的服务器上,用于向域名为127.0.0.1:1002的服务器发送数据:
<!DOCTYPE html>
<html lan上g="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
fuwfuq
<body>
<h1>server1</h1>
<iframe id="myframe" src="http://127.0.0.1:1002/1002.html" frameborder="0"></iframe>
<script>
myframe.onload = function () { //注意要onload之后,才可以用postMessage
let iframeWindow = myframe.contentWindow;
iframeWindow.postMessage("宁静致远", "http://127.0.0.1:1002/1002.html");
}
//监听B传递的信息
window.onmessage = function (ev) {
console.log(ev.data);
}
</script>
</body>
</html>
这里有个需要注意的问题,我们 发送方的引用,即调用postMessage() 那个引用的源要与接受方的源即postMessage()的第二的参数 要同源。
1002.html,这个文件跑在域名为127.0.0.1:1002的服务器上,用于向域名为127.0.0.1:1001的服务器发送和接收数据:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>server2</h1>
<script>
//监听A发送过来的信息
window.onmessage = function (ev) {
console.log("server2 recevice message");
ev.source.postMessage("recevice message", ev.origin);
}
</script>
</body>
</html>
结果:
这种方法本质是在一个页面上通过iframe标签打开不同源的其它页面,通过主页面与iframe交流的方法postMessage进行数据的交流。可以进行get和post请求。
6.WebSocket协议跨域
总结
前端跨域方案可以分为4个方向:
- 通过没有同源策略限制的Html标签实现数据请求。
- 在客户端建一个服务器代理。
- 请求加上access-control-change- 请求头。
- 使用websocket协议。