前端开发中,跨域使我们经常遇到的一个问题,也是面试中经常被问到的一些问题,所以,总结以下常用的解决跨域的方式。
什么是跨域?
跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。
同源策略:所谓的同源是指,域名、协议、端口均为相同,当这些有一个不相同时会产生跨域。
同源策略限制了一下行为:
-
Cookie、LocalStorage 和 IndexDB 无法读取
-
DOM 和 JS 对象无法获取
-
Ajax请求发送不出去
如何解决跨域?
1、jsonp跨域
jsonp跨域其实也是JavaScript设计模式中的一种代理模式。在html页面中通过相应的标签从不同域名下加载静态资源文件是被浏览器允许的,所以我们可以通过这个“犯罪漏洞”来进行跨域。一般,我们可以动态的创建script标签,再去请求一个带参网址来实现跨域通信。
//动态创建一个script标签
var spt = document.createElement('script');
//设置请求路径,这里word=1为参数
spt.src = 'https://sug.so.360.cn/suggest?callback=suggest_so&encodein=utf-8&encodeout=utf-8&format=json&fields=word&word=1';
//添加至body里面
document.body.appendChild(spt);
//在url中可以看出,请求该网址的回调函数为:suggest_so (callback后面指定了回调函数名字,必须使用此名字还可以识别)
function suggest_so(data) {
console.log(data);
}
为了方便使用 jQuery 提供了jsonp的请求方式
$.ajax({
url: 'https://sug.so.360.cn/suggest',
type: 'GET',
dataType: 'jsonp', //此处设置请求方式为jsonp
jsonpCallback: 'suggest_so',
data: {
word: 1
},
success: function (data) {
console.log(data);
}
})
虽然这种方式非常好用,但是一个最大的缺陷是,只能够实现get请求
2、跨域资源共享 CORS 目前主流的跨域解决方案
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。IE8+:IE8/9需要使用XDomainRequest对象来支持CORS。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。 因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
以node(express框架)服务器为例:
在app.js 文件内添加以下代码:
var app = express();
/*为app添加中间件处理跨域请求*/
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*'); //标识允许哪个域的请求,*为所有域
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS'); //标识允许的请求方式
res.header("Access-Control-Allow-Headers", "X-Requested-With"); //设置请求头需要带的参数
res.header('Access-Control-Allow-Headers', 'Content-Type'); //设置请求头需要带的参数
next();
});
由于一般都是服务端处理,作为前端就不再深究。
3、node代理跨域
利用node + express + http-proxy-middleware搭建一个proxy服务器。
proxy代理服务器为 http://localhost:2000,目标服务器为 http://localhost:3000
在app.js里面添加以下代码:
var proxy = require('http-proxy-middleware');
const apiProxy = proxy({
target: 'http://localhost:3000',
changeOrigin: true,
xfwd: true
}); //将服务器代理到 http://localhost:3000
app.use('/home', apiProxy); //home子目录下的都是用代理
注意:以上代码必须写在下面的代码的上面,否则post请求不能被很好的代理
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({extended: false}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
代理调用:
$.ajax({
type:'post',
data:JSON.stringify({
password: '123456'
}),
headers: {
'content-type': 'application/json'
},
url:'http://localhost:2000/home/test',
success:function(data){
console.log(data);
},
error:function(data){
console.log(data);
}
})
4、nginx代理跨域
nginx代理和node代理差不多,修改nginx.conf文件
server {
# 监听5050端口
listen 5050;
# 服务ip
server_name localhost;
# 设置资源文件夹路径
location / {
root C:/Users/yunhou/Desktop/test;
index index.html index.htm;
add_header Access-Control-Allow-Origin *;
}
# 设置代理 代理所有/api的接口
# 注意:如果所代理的接口没有/api需要截去,则在代理路径后面加/
# 注意:如果需要保留原接口路径,则不加/
# 例子:访问http://localhost:3000/api/test
location /api {
# 此处最终解析路径为 http://localhost:3000/home/test
# proxy_pass http://localhost:3000/home/;
# 此处最终解析路径为 http://localhost:3000/home/api/test
proxy_pass http://localhost:3000/home;
}
}
重新启动服务器即可
nginx.exe -s reload