为什么要跨域
跨域问题是由浏览器的同源策略所导致的。
何为同源?
protocol(协议)、domain(域名)、port(端口)三者一致。
如何解决跨域
1.JSONP
JSONP利用了script img link等标签没有跨域限制的特性,只支持GET方法。
流程:
1.前端定义全局cb
2.加在请求后面,声明执行函数callback=cb
3.后端获取cb,带上参数并以执行函数的方式传回前端
//后端
const Koa = require("koa");
const fs = require("fs");
const app = new Koa();
app.use(async (ctx, next) => {
if (ctx.path === "/api/jsonp") {
const { cb, msg } = ctx.query;
ctx.body = `${cb}(${JSON.stringify({ msg })})`;
return;
}
});
app.listen(8080);
//前端
<script type="text/javascript">
window.cb = function(res) {
console.log(res);
};
</script>
<script type="text/javascript"
src="http://localhost:8080/api/jsonp?msg=hello&callback=cb">
</script>
//AJAX版本,实际上就是自动创建一个全局cb,即success的function
<script>
$.ajax({
url: "http://localhost:8080/api/jsonp",
dataType: "jsonp",
type: "get",
data: {
msg: "hello"
},
jsonp: "cb",
success: function(data) {
console.log(data);
}
});
</script>
2.CORS
使用额外的HTTP头,向不同源的服务器发起请求。
目前的HTTP请求一般在设置了一些请求头之后都是非简单请求,会触发OPTION 预检。
需要服务器配合。
app.use(async (ctx, next) => {
ctx.set("Access-Control-Allow-Origin", ctx.headers.origin);
ctx.set("Access-Control-Allow-Credentials", true);
ctx.set("Access-Control-Request-Method", "PUT,POST,GET,DELETE,OPTIONS");
ctx.set(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, cc"
);
if (ctx.method === "OPTIONS") {
ctx.status = 204;
return;
}
await next();
});
cookie的使用tip: 需要在头部加上withCredentials=true,且服务端Access-Control-Allow-Origin为非*
一般情况下后端接口是不会开启这个跨域头的,除非是一些与用户无关的不太重要的接口。
3.HTTP Proxy
最常用的就是webpack-dev-server(使用了http-proxy库)
客户端代理, 代理客户端, 服务端不知道实际发起请求的客户端。
利用了服务器请求服务器不存在跨域,相当于模拟了一个nginx服务请求
//webpack.config.js
module.exports = {
//其他配置
devServer: {
port: 8000,
proxy: {
"/api": {
target: "http://localhost:8080"
}
}
},
};
//前端请求方式,不带域名,现在是同域请求
axios.get("/api/getSomething").then(res => {
console.log(res.data);
});
4.Nginx反向代理
反向代理即是服务端代理, 代理服务端, 客户端不知道实际提供服务的服务端。
与前端无关,需要配置nginx
www.aaa.cn -> www.aaa.com // 从aaa.cn源来,进行反向代理
//proxy服务器
server {
listen 80;
server_name www.aaa.com; //当前服务器
location / {
proxy_pass www.aaa.cn; //反向代理
proxy_cookie_domain www.aaa.cn www.aaa.com;
add_header Access-Control-Allow-Orgin www.aaa.cn;
add_header Access-Control-Allow-Credentials true
}
}
5.Websocket
是H5的一套规范,让Client和Server之间进行持久连接,没有跨域限制。
//前端部分
let socket = new WebSocket("ws://localhost:8080");
socket.onopen = function() {
socket.send("socket建立了,发送这条消息");
};
socket.onmessage = function(e) {
console.log('收到服务器给我的消息了', e);
};
//后段部分
const WebSocket = require("ws");
const server = new WebSocket.Server({ port: 8080 });
server.on("connection", function(socket) {
socket.on("message", function(data) {
socket.send(data);
});
});
以下大都是多窗口之间的数据传输
6.window.postMessage
a.html
<iframe
src="http://localhost:8080"
frameborder="0"
id="iframe"
onload="load()"
></iframe>
<script>
function load() {
iframe.contentWindow.postMessage("从a.html发出的消息", "http://localhost:8080");
window.onmessage = e => {
console.log(e.data);
};
}
</script>
b.html
<script>
window.onmessage = e => {
console.log(e.data); // 从a.html发出的消息
e.source.postMessage(e.data, e.origin);
};
</script>
7.document.domain + iframe
只用于二级域名相同情况,都加上document.domain ='second.com'即可获取iframe.contentWindow下的全局变量
8.window.location.hash + iframe
通过 url 带 hash ,通过一个非跨域的中间页面来传递数据。
9.window.name + iframe
当location变化,重新加载后,它的name属性可以依然保持不变。