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

280 阅读3分钟
原文链接: shawchen08.github.io

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

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

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

devServer.proxy 简介

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

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

基础用法

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

// webpack.config.js

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

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

重写路径

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

// 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 上,且使用了无效证书的后端服务器。如果你想要接受,修改配置如下:

// webpack.config.js

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

选择性代理

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

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

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

// 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 属性的对象」构成的数组:

// webpack.config.js

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

跨域支持

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

// 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"',我们改造一下:

// /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 地址和联调地址的切换

// /package.json
{
  ...
  "scripts": {
    ...
    "api": "npm run dev --api"
  },
  ...
}
// /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