目录
一、背景
浏览器因为安全问题有跨域限制,不允许跨域请求资源。所谓同源是指域名、协议、端口完全相同,只有同源的地址才可以相互通过ajax的方式请求。
前后端分离开发的模式下,一般我们的前端资源和后端服务资源会分开部署,这样对系统的架构、负载、吞吐等都有好处,而且也便于扩展。这样就会存在一个跨域问题。
二、前端解决跨域方案
1、JSONP
原理
浏览器不允许跨域请求资源,但允许跨域群请求文件。JSONP主要是利用script的src标签链接外部js文件,以函数调用的形式,将要传递的数据作为函数参数进行传递。
优点
适用于所有浏览器,实现简单。
缺点
只能使用get请求,比较局限,现在不怎么使用了。
2、CORS(跨域资源共享)
原理
普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。
需注意的是:由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,而非当前页。
实现
浏览器对于cors请求分为两类:简单请求和非简单请求。
1)简单请求
浏览器直接发出cors请求,在头信息中添加origin字段(协议、端口、域名)。
如果origin在服务器指定的源许可范围内,就会返回几个头信息字段:
Access-Control-Allow-Origin: <必选> 表示服务器允许的请求源 *表示所有源
Access-Control-Allow-Credentials: <可选> true表示允许发送cookies
Access-Control-Expose-Headers: <可选> 调用getResponseHeader()可以获取的其它字段
2)非简单请求
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求。
优点
支持所有的HTTP请求,基本不需要前端配置,是当前的主流跨域解决方案。
缺点
IE7及以下不兼容。
具体内容可查看:阮一峰的网络日志:跨域资源共享CORS详解
3、nodejs中间件代理跨域
原理
浏览器有同源限制,服务器之间没有。通过中间代理服务器进行请求转发。
实现
node的中间件代理,基础代码可以查看我之前写的一篇博客:使用node服务器简单启动本地项目。在这个的基础上加入代理中转Proxy代码,将请求转发至最终的服务器。
更多情况下,我们前端使用vue-cli脚手架搭建项目,那实现起来就更加方便了。因为webpack中就提供了http-proxy-middleware中间件。所以下面我们就主要讲解在vue-cli项目中的操作。
这里我们先用node.js启一个本地服务器接口作为测试:
// server.js
// 引入express中间件
let express = require('express');
let app = express();
// 定义api 发送数据
app.get('/api', (req, res) => {
res.json({ name: 'hahaha' })
})
app.get('/test', (req, res) => {
res.json({ name: 'this is a test' })
})
// 监听服务端口
app.listen(8888, () => {
console.log('web server running at http://localhost:8888');
});
然后控制台运行node server.js。控制台出现web server running at http://localhost:8888,即表示启动成功。浏览器中输入http://localhost:8888/api和http://localhost:8888/test都可查看我们相对应的数据。
接下来回到我们vue-cli构建的项目,由于我们的项目运行在http://localhost:8000下,如果直接向http://localhost:8888/api发送请求的话,会报跨域错误。这个时候就可以使用proxy做中转。
相关代码如下:
// vue.config.js中添加配置
module.exports = {
// ...
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8888', // 代理跨域目标地址
changeOrigin: true,
// pathRewrite: { // 可选 地址重写
// '^/api': ''
// }
}
}
}
}
// App.vue 使用axios发送请求测试 注意:此处我将axios作为$http挂载在全局了
export default {
// ...
async created() {
let data = await this.$http.get('/api');
console.log(data);
}
}
查看控制台:可以看到接口返回data数据正确,我们的请求被成功处理了。
查看network也是正常的:
优点
搭配vue-cli脚手架使用简单方便。
缺点
只能用于开发环境,项目要打包上线到服务器上就无法使用了。因此建议使用nginx反向代理,它俩解决跨域问题的原理基本一致。
4、Nginx反向代理
这里我们先介绍一下正向代理和反向代理的概念:
正向代理:
正向代理就像一个跳板,它代理的客户端,隐藏真实客户端。
用户无法访问外部资源,可以先访问代理服务器,由它去访问目标地址,然后将内容再转给我们。
从网站的角度,只在代理服务器来取内容的时候有一次记录,有时候并不知道是用户的请求,也隐藏了用户的资料,这取决于代理告不告诉网站。
反向代理:
对于客户端而言,它就像是原始服务器,客户端不需要进行任何设置。它代理的是服务端,隐藏真实服务端。
比如我想访问 http://www.test.com/readme,但www.test.com上并不存在readme页面,于是他是偷偷从另外一台服务器上取回来,然后作为自己的内容返回用户,但用户并不知情。这里所提到的 www.test.com 这个域名对应的服务器就设置了反向代理功能。
原理
ngnix反向代理正是通过ngnix配置一个代理服务器,以此来解决跨域问题。
实现
1)本地电脑windows下安装nginx
下载解压后文件夹目录如下:
其中html文件夹下就是用来放我们的项目的。
2)启动nginx
a、安装解压后,cmd进入到解压后的文件夹中(快捷进入法:打开cmd,输入cd空格,然后将文件夹拉入cmd窗口,回车即可;或者在文件夹下运行git bash);
b、运行相关指令
启动nginx:start nginx
重载nginx(nginx配置更新后):nginx -s reload
停止nginx:nginx -s stop或者nginx -s quit
我们打开浏览器输入:localhost:80(默认地址),如何显示welcome to nginx,则表示启动成功。
3)将生产文件放在html文件夹下
接着第3点node中间件代理跨域,我们将vue-cli的项目运行npm run build指令生成dist文件夹。
然后将dist文件夹放入nginx的html文件夹中。然后就可以在浏览器中查看项目:
控制台可以看到,axios请求无法找到http://localhost:80/api接口地址,产生报错。这个主要是因为我们开发的时候写的是’/api’相对地址,所以在生产环境下自动加上了前缀域名。但实际上,我们如果写成http:localhost:8888/api绝对地址的话,就会报跨域的错误。
4)配置反向代理
nginx文件夹下打开配置文件,文件路径:conf > nginx.conf进行相关配置:
// ...
server {
listen 80; // nginx服务器启动后默认监听端口
server_name localhost; // nginx服务器名称
// 反向代理访问远程后台接口的配置
location /api {
proxy_pass http://localhost:8888; // 反向代理地址
}
}
然后运行nginx -s restart重启nginx,然后刷新localhost/dist网页进行查看。
控制台:
network:
代理成功!
优点
堪称最完美的解决跨域的方案吧。当然,ngnix本身还有其它的很多优点,可以看看其它资料介绍。
缺点
缺点大概就是要配置一下吧…
参考资料
真是要加油学习呢~
[1]: 不要再问题跨域问题了
[2]: b站视频:ngnix反向代理和负载均衡教程
[3]: vue中跨域以及部署nginx跨域设置