Access to XMLHttpRequest at 'http://etrip63.eavic.com/api/standardRule/carpage?name=&publishState=&createId=&createTime=&pag geSize=10&pageNum=1' from origin http://localhost:8080' has been blocked CORS policy: Response to preflight request
doesn't pass access control check: Redirect is not allowed for a preflight request.
相信这段话小伙伴们不陌生吧,我们的第一反应就是跨域.
首先了解一下跨域的产生:
浏览器遵循同源策略(协议【http/https】、域名【A.com/B.com】、端口【80/8080】三者必须相同),同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的安全机制。比如在域名为A.com的网站中去请求B.com中的数据的时候,因为域名不同,不符合同源策略就会产生跨域,浏览器就会抛出上面的错误。
同源策略主要限制在:
1.Cookie、LocalStorage、IndexDB无法读取;
2.DOM无法获得,比如document.body等都是拿不到的;
3.AJAX请求无法正常完成(AJAX可以发送,但浏览器拒绝响应);
其次,说一下跨域的几种解决方案:
1.JSONP:script标签不在同源策略限制内
//首先要和后端定义好返回的callback名称
//使用script的callback=func传递到服务端,服务端返会func({data:123}),
//本地再调用func(data){data},此方法兼容性好,但是只支持get请求
eg:
<script>
var foo = function(data){
console.log(data)
}
</script>
<script src="JSONP.js"></script>
JSONP.js文件代码:
foo({
'ip': '1.1.1.1'
});
2.Hash:url#后面的hash值变动,页面不刷新
//在当前aa页面,获取iframe
<script>
let b=document.getElementsByTagName('iframe');
b.src=b.src+'#'+'data';//此处data可以用 JSON.stringify() 传递
</script>
//页面bb
<script>
window.onhashchange=function(){
let data=window.location.hash;
}
</script>
3.postMessage
<script>
//第二个参数可以给多个窗口传参,但是不安全
BWindow.postMessage('data','b.com');
</script>
//B窗口中接收
<script>
window.addEventListener('message',function(event){
console.log(event.origin);
},false)
</script>
4.WebSocket
<script>
//发送请求
//ws不加密 wss加密
let wss=new WebSocket('wss://a.com');
wss.onopen=function(event){
wss.send('send data');
};
//接收请求
wss.onmessage=function(event){
console.log(event.data);
wss.close();//断开连接
};
//监听关闭
wss.onclose=function () {
console.log('do something...')
}
</script>
5.CORS:在请求头中加入Origin允许通信
整个CORS的通信过程,都是浏览器自动完成,不需要用户参与。
实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨域通信。
eg:Node
ctx.set('Access-Control-Allow-Origin', '*');
ctx.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, HEAD');
ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length,authorization,
Accept, X-Requested-With');
eg:PHP
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: *");
CORS又分为简单请求和复杂请求
简单请求:
1.使用GET、POST、HEAD;
2.Content-Type的值是text/plain、multipart/form-data、application/x-www-form-urlencoded;
3.请求头是Accept、Accept-Language、Content-Language、Content-Type
4.任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器;
5.没有使用 ReadableStream 对象
反之则是复杂请求.
在复杂请求下通信,设置了Access-Control-Allow-Origin也会报跨域的错误.是因为在请求之前,增加一次HTTP查询请求,成为预检请求,该请求方法是OPTIONS,通过该请求来知道服务器是否允许跨域请求。如果我们在服务端不做处理,前端浏览器依然会抛出跨域的报错。所以需要处理以下预检请求OPTIONS:
if (ctx.method === 'OPTIONS') {
ctx.body = 204;
}
6.Nginx配置
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
}
7.webpack配置跨域(只适用于开发环境)
eg:vue.config.js
module.exports = {
//...
devServer: {
proxy: {
//比如API请求/api/users, 会被代理到请求 www.baidu.com/api/users
'/api': {
target: 'http://www.baidu.com/',//可以是域名可以是IP地址
pathRewrite: {'^/api' : ''},//给代理命名后,在访问时把命名删除掉
changeOrigin: true,// target是域名的话,需要这个参数,
secure: false,// 设置支持https协议的代理
},
'/api2': {
.....
}
}
}
};