公司有一个项目的后端正在陆陆续续重构着,期间需要将新旧 API 一起混用。而且我们都知道,Cookie 默认有同源策略,我们本地开发的环境下,域名地址一般为 localhost127.0.0.1,而直接请求其他站点的接口,即便它返回了 Set-Cookie 头,你也会发现并没有作用。

注意:本篇教程适用于 Vite V2 版本,V3 版本不适用

使用反向代理

想要解决这类问题最简单的办法就是设置反向代理,使得多个来自不同站点的 API 聚合在同一个站点下。在服务器环境下最常见的解决方法就是使用 Nginx 的虚拟主机配置来实现,但在本地开发环境下这种操作就显得略微复杂,而且并不是所有前端都会用 Nginx(偏后端/运维)。幸运的是,ViteJS 内置了基于 node-http-proxy 实现的反向代理功能,平时开发我基本上都在用。

这是来自 官方文档 的示例,其中有一段写了 rewrite 参数的配置:

export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://jsonplaceholder.typicode.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
})

发现了问题

我起初也是参考着这个来的,写了一段将本地 /api 指向 https://paul.ren/api(脱敏)这样的配置,当我想要增加一个 /apiNew 的代理时,发现它并不起作用。

本文有些水,说白了,就是一条“小杠”酿大祸,想看结果可以直接往下跳过...

"/api": {
  target: "https://paul.ren/api",
  changeOrigin: true,
  rewrite: (path) => path.replace(/^\/api/, "")
},
"/apiNew": {
  target: "https://v2.paul.ren/api",
  changeOrigin: true,
  rewrite: (path) => path.replace(/^\/apiNew/, "")
}

然后页面访问 http://localhost:3300/apiNew/login(脱敏)接口后返回 404,这就奇怪了,为什么失败了呢?

排查流程

我首先想排查下 rewrite 参数干了什么。可 ViteJS 的文档上也没有给具体的说明,就翻了翻其 源码,看起来是把我访问路由的 path 给替换了一波。

访问 `http://localhost:3300/apiNew/login`
得到 path `/apiNew/login`
rewrite 就是把 `/apiNew/login` 的 前缀去掉,变成 `/login`,
最后得到代理地址 `https://v2.paul.ren/api/login`

看起来我的设置是正确的,可实际访问确实如此吗?尝试过在配置项里面写 console.log 函数,没有用,就想着能不能让 ViteJS 把所有信息打印出来,这种情况肯定是属于异常行为了。

开启日志输出

在官方文档用关键词 debug 未果,换了个 log 去搜,有了新的答案,logLevel

这个参数同样也在 vite.config.ts 里面设置,默认是 info,我改成 warn 之后,报错信息果然就出来了。

vite:proxy /apiNew/captcha/ -> https://paul.ren/api +0ms

可以看到,我访问 /apiNew,实际上走了 /api 的规则!一切水落石出了!这么一看,/apiNew 还确实符合 /api 这个规则...

解决方法

那解决办法就很清楚了,我把 /api 这条规则的适配改成 /api/,就无法匹配 /apiNew 了,最终我的配置项改成了这样子,问题也就成功解决咯!

"/api/": {
  target: "https://paul.ren/api/",
  changeOrigin: true,
  rewrite: (path) => path.replace(/^\/api/, "")
},
"/apiNew/": {
  target: "https://v2.paul.ren/api/",
  changeOrigin: true,
  rewrite: (path) => path.replace(/^\/apiNew/, "")
}

还有一个方法就是把这两个规则互相对调,也就是让 /apiNew 优先级更高,更快被匹配到,但是这样感觉做就是埋坑行为,规则一旦多起来就很麻烦了...

番外话

其实最开始是公司的实习生发现后端接口的 Cookie 没法设置上去,就私下开了个腾讯会议问我,我让她按我的方法设置反向代理之后就触发了这样一个问题了。说实话我对这个问题还蛮有兴趣,毕竟从一开始我就没明白 rewrite 这个参数是怎么用的,今天这样一出虽然耗费了将近一小时左右的时间,而且最终解决方案及其「简单粗暴」。但我感觉还是有些许意义的,以后遇到类似问题就多了一个好排查的点了。