利用 webpack 的 devServer.proxy 实现开发阶段的跨域处理

前后端分离模式下,前端在开发阶段利用 mock 服务降低了对后台的依赖,到了联调阶段,突然发现所有请求都跨域了,这时候有两条路:

  1. 让后台开启跨域支持
  2. 前端通过代理支持跨域

这里要讲的就是第二条路,由于项目是基于 vue-cli 构建的,所以通过 webpackdevServer.proxy 来实现跨域支持。

devServer.proxy 简介

先简单介绍一下 devServer.proxy 的用法:

注: 这里的 devServer.proxy 就是 vue-cli 中位于 /config/index.jsdev.proxyTable,后面不再赘述

基础用法

localhost:3000 上有后端服务的话,你可以这样启用代理:

1
2
3
4
5
6
7
8
9
10
11
// webpack.config.js

module.exports = {
...
devServer: {
proxy: {
'/api': 'http://localhost:3000'
}
}
...
}

这时请求 /api/users 会被代理到请求 http://localhost:3000/api/users

重写路径

如果你不想始终传递 /api ,则需要重写路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// webpack.config.js

module.exports = {
...
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: { '^/api' : '' } // remove base path
// pathRewrite: { '^/api' : '/api/new-path' } // rewrite path
}
}
}
...
}

这时请求 /api/users 会被代理到请求 http://localhost:3000/users

HTTPS 支持

默认情况下,不接受运行在 HTTPS 上,且使用了无效证书的后端服务器。如果你想要接受,修改配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// webpack.config.js

module.exports = {
...
devServer: {
proxy: {
'/api': {
target: 'https://other-server.example.com',
secure: false
}
}
}
...
}

选择性代理

有时你不想代理所有的请求。可以基于一个函数的返回值绕过代理。

在函数中你可以访问请求体、响应体和代理选项。必须返回 false 或路径,来跳过代理请求。

例如:对于浏览器请求,你想要提供一个 HTML 页面,但是对于 API 请求则保持代理。你可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// webpack.config.js

module.exports = {
...
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
bypass: function(req, res, proxyOptions) {
if (req.headers.accept.indexOf('html') !== -1) {
console.log('Skipping proxy for browser request.');
return '/index.html';
}
}
}
}
}
...
}

代理多个路径

如果你想要代理多个路径特定到同一个 target 下,你可以使用由一个或多个「具有 context 属性的对象」构成的数组:

1
2
3
4
5
6
7
8
9
10
11
12
// webpack.config.js

module.exports = {
...
devServer: {
proxy: [{
context: ['/auth', '/api'],
target: 'http://localhost:3000',
}]
}
...
}

跨域支持

大部分情况都会涉及跨域问题,我们可以通过设置 changeOrigin: true 实现跨域支持:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// webpack.config.js

module.exports = {
...
devServer: {
proxy: {
'/api': {
target: 'https://other-server.example.com',
changeOrigin: true
}
}
}
...
}

基于 vue-cli 的跨域处理

回到最开始的问题,基于 vue-cli 的跨域处理,我们只要将上述相关配置写到 /config/index.jsdev.proxyTable 中即可,唯一需要注意的问题是我们可能在 /config/dev.env.js 中配置了 BASE_API: '"http://your-mock-server:port"',我们改造一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// /config/dev.env.js
...
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
BASE_API: '"/"'
})

// /config/index.js
...
module.exports = {
dev: {
...
proxyTable: {
'/api': {
target: 'http://your-api-server:port/', // 后端服务地址
changeOrigin: true
}
},
...
},
...
}

这样就实现了前后端联调时出现的跨域问题

我们完善一下,通过命令行传参来实现 mock 地址和联调地址的切换

1
2
3
4
5
6
7
8
9
// /package.json
{
...
"scripts": {
...
"api": "npm run dev --api"
},
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// /config/index.js
...
module.exports = {
dev: {
...
proxyTable: {
'/api': {
// 通过命令区分后端服务地址和 mock 地址
target: process.env.npm_config_api ? 'http://your-api-server:port/' : 'http://your-mock-server:port/',
changeOrigin: true
}
},
...
},
...
}

我们自己开发时运行 npm run dev,前后端联调时运行 npm run api

参考资料

webpack 官网 devServer.proxy 配置
http-proxy-middleware