有一个项目的后端正在陆陆续续重构着,期间需要将新旧 API 一起混用。而且我们都知道,Cookie 默认有同源策略,我们本地开发的环境下,域名地址一般为 localhost
或 127.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
这个参数是怎么用的,今天这样一出虽然耗费了将近一小时左右的时间,而且最终解决方案及其「简单粗暴」。但我感觉还是有些许意义的,以后遇到类似问题就多了一个好排查的点了。