前话
在前后端分离模式开发项目、部署时,总会不可避免的碰到跨域问题,前前后后也查了不少资料,用了不少解决方案,能解决,但总感觉不太适合。
在经过多番查询、实践,日积月累下也慢慢摸索出一套解决方案。再结合项目部署场景,统一做一次记录。
问题复现
我们在开发时,将前后端分离项目分为 两个前端(客户端、管理端)、一个后端(服务端)
,两个前端各尽其责,由后端进行统筹。
如此一来,一个项目就启动了三个端口服务,如下:
- 开发环境:
- 前端—客户端,端口:
100
- 前端—管理端,端口:
200
- 后端—服务端,端口:
300
- 前端—客户端,端口:
build后,在线上环境中,服务端依然需要启动一个端口进行服务,如下:
- 生产环境:
- 前端—客户端,端口:
80
- 前端—管理端,端口:
80
- 后端—服务端,端口:
300
- 前端—客户端,端口:
由上述可见,有下列问题需要解决:
- 开发、生产环境中端口不同导致的跨域问题;
- build后,本地运行;
- 生产环境中客户端与服务端(包括其他项目)同时使用80端口;
- 一台服务器部署多个项目;
统筹一番
考虑到多个项目,我们将每个项目进行了一番规范
项目 Api规范
每个项目,api 根路径与项目名关联,二级路径与本身应用相关联,举栗:
-
例 1:摩托商城项目 motorcycle-mall:
- 前端 — 管理端:请求路径:
/moto-mall/mgr/...
- 前端 — 客户端:请求路径:
/moto-mall/c/...
- 外部 — 微信公众号:请求路径:
/moto-mall/wxmp/...
- 外部 — Alipay:请求路径:
/moto-mall/alipay/...
- …
- 以上请求将统一发送给后端,由后端接收并处理:
- 后端 — 服务端:
- 接收的请求路径:
/moto-mall/...
- 收到请求后,再通过二级路径确定来自哪里的请求,如:
/moto-mall/mgr/...
来自管理端的请求/moto-mall/c/...
来自客户端的请求/moto-mall/wxmp/...
来自公众号的请求- …
- 接收的请求路径:
- 前端 — 管理端:请求路径:
-
例 2:汽车商城项目 car-mall:
- 前端 — 管理端:请求路径:
/car-mall/mgr/...
- 前端 — 客户端:请求路径:
/car-mall/c/...
- 外部 — 微信公众号:请求路径:
/car-mall/wxmp/...
- 外部 — Alipay:请求路径:
/car-mall/alipay/...
- …
- 以上请求将统一发送给后端,由后端接收并处理:
- 后端 — 服务端:
- 接收的请求路径:
/car-mall/...
- 收到请求后,再通过二级路径确定来自哪里的请求,如:
/car-mall/mgr/...
来自管理端的请求/car-mall/c/...
来自客户端的请求/car-mall/wxmp/...
来自公众号的请求- …
- 接收的请求路径:
- 前端 — 管理端:请求路径:
解决问题
一、开发环境中跨域
举例,如下项目:
- 后端 — 服务端,端口:
300
,请求路径:/car-mall/c/...
- 前端 — 客户端,端口:
200
,请求路径:/car-mall/mgr/...
- 前端 — 管理端,端口:
100
,接收请求:/car-mall/...
前端(客户端和管理端)配置 vue.config.js
文件:
// 将请求路径代理发送到服务端端口
module.exports = {
devServer: {
open: true,
port: 100 or 200, // 客户端或管理端的端口,两个前端应用都需要配置该代理
proxy: {
/*
process.env.VUE_APP_BASE_API:
.env.development 和 .env.production 以及 .env.test 文件中配置的路径
根据此处例子:
管理端为:/car-mall/mgr
客户端为:/car-mall/c
*/
[process.env.VUE_APP_BASE_API]: {
target: 'http://localhost:300' + process.env.VUE_APP_BASE_API, // 目标路径,管理端端口为300,这里将会把请求直接发到300端口
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: '' // 路径重写
}
}
}
}
}
proxy 代理后,后端无需配置CrossOrigin,能正常收到请求,且不存在跨域问题。
二、build 后,本地运行以及跨域
前端项目 build 后,默认向 80 端口请求数据,与服务端依然不在同一端口,还是存在跨域问题。
若需要在本地运行已 build 项目,我们使用Nginx
代理,并将 80 端口的请求分发给服务端。
配置nginx\conf\nginx.conf
文件:
http {
# .......
# 其他配置
# .......
server {
listen 80; # 监听80端口
server_name localhost; # 当前服务的域名
root E:\\project-dev\\dist-test; # http://localhost指向的目录,将build后的前端项目放入该目录
# 汽车商城配置
# 将80端口接收到的 /car-mall/... 路径请求,代理到对应的服务端
location ^~/car-mall {
rewrite ^/car-mall/(.*)$ /car-mall/$1 break; # 无特殊情况不需要重写,这里可以注释掉。
proxy_pass http://localhost:300; # 汽车商城服务端端口为300
}
# 摩托商城配置
# 将80端口接收到的 /moto-mall/... 路径请求,代理到对应的服务端
location ^~/moto-mall {
rewrite ^/moto-mall/(.*)$ /moto-mall/$1 break; # 无特殊情况不需要重写,这里可以注释掉。
proxy_pass http://localhost:301; # 摩托商城服务端端口为301
}
}
}
配置完成后start nginx
启动 nginx ,若已启动则nginx -s reload
重载配置。
将 build 后的前端放入 Nginx 配置中的 root 目录,启动服务端。
最后浏览器中输入 http://localhost
即可完成本地运行。
三、生产环境中跨域以及多项目部署
原理与本地运行大致相同,区别在于需要让 客户端、管理端 分别使用不同的域名访问,并且都是通过默认 80 端口,然后再将请求发给对应的服务端。
-
1、汽车商城:
- 前端 — 客户端:
- 域名:
carmall.abc.com
- 请求路径:
/car-mall/c/...
- 域名:
- 前端 — 管理端:
- 域名:
carmalladmin.abc.com
- 请求路径:
/car-mall/mgr/...
- 域名:
- 后端 — 服务端:
- 端口:
300
- 端口:
- 前端 — 客户端:
-
2、摩托商城:
- 前端 — 客户端:
- 域名:
motomall.abc.com
- 请求路径:
/moto-mall/c/...
- 域名:
- 前端 — 管理端:
- 域名:
motomalladmin.abc.com
- 请求路径:
/moto-mall/mgr/...
- 域名:
- 后端 — 服务端:
- 端口:
301
- 端口:
- 前端 — 客户端:
配置nginx\conf\nginx.conf
文件:
http {
# .......
# 其他配置
# .......
# 汽车商城---客户端配置
server {
listen 80; # 监听80端口
server_name carmall.abc.com; # 汽车商城---客户端域名
root E:\\mall\\car\\client; # http://carmall.abc.com 指向的目录,客户端应用放入该目录
index index.html;
location ^~/car-mall { # 将 汽车商城---客户端 的请求代理到服务端
proxy_pass http://localhost:300;
}
}
# 汽车商城---管理端配置
server {
listen 80; # 监听80端口
server_name carmalladmin.abc.com; # 汽车商城---管理端域名
root E:\\mall\\car\\admin; # http://carmalladmin.abc.com 指向的目录,管理端应用放入该目录
index index.html;
location ^~/car-mall { # 将 汽车商城---管理端 的请求代理到服务端
proxy_pass http://localhost:300;
}
}
# 摩托商城---客户端配置
server {
listen 80; # 监听80端口
server_name motomall.abc.com; # 摩托商城---客户端域名
root E:\\mall\\moto\\client; # http://motomall.abc.com 指向的目录,客户端应用放入该目录
index index.html;
location ^~/moto-mall { # 将 摩托商城---客户端 的请求代理到服务端
proxy_pass http://localhost:301;
}
}
# 摩托商城---管理端配置
server {
listen 80; # 监听80端口
server_name motomalladmin.abc.com; # 摩托商城---管理端域名
root E:\\mall\\moto\\admin; # http://motomalladmin.abc.com 指向的目录,管理端应用放入该目录
index index.html;
location ^~/moto-mall { # 将 摩托商城---管理端 的请求代理到服务端
proxy_pass http://localhost:301;
}
}
}
配置完成后start nginx
启动 nginx ,若已启动则nginx -s reload
重载配置。
启动服务端。
完成。