背景
我最近在参与一个协作项目的开发,这个项目可以多人协作,每次启动项目的时候,都需要到线上去复制一个已经登录用户的 token,然后设置到本地,才能正常开发。本地 mock 数据是不大可能的,因为需要测试多个用户之间协作,所以需要连接 server,就导致每次 token 过期就很麻烦,复制 token -> 重启 server(但是进度比较赶,可能大家觉得可以忍受,然后我忍不了,就着手解决了)。
解决思路
在 proxy 中增加获取 token 的步骤,获取到 token 后设置到 request header 中。 由于 token 获取需要登录 cookie(介绍一下,用户有统一的登录认证,但是本项目的 token 是独立的,需要在用户登录后才能获取),所以我还需要先登录,到这大概思路有了,下面用一个图介绍一下。
所以到这就有关键的一步,怎么完成获取 获取 cookie 以及获取 token 这一步。
webpack-dev-server
我最开始是关注到 webpack-dev-server
的 before
和 after
两个配置项(现在新版的应该是OnBeforeSetupMiddleware
和 OnAfterSetupMiddleware
),我可以在这去拦截请求,然后获取 token
,这两个配置项都是 webpack-dev-server
暴露出来的 hooks
,在这两个配置项里面,我们可以拿到当前 webpack-dev-server
的实例,前面都是我看文档得到的,然后因为我想进一步了解,所以我就去了他的仓库里面看了下具体代码,这里贴一下链接,然后看到了下面的代码。
setupOnAfterSetupMiddlewareFeature() {
this.options.onAfterSetupMiddleware(this);
}
看到 this
之后,我们在文档里面看到
这个用法,是不是很像 node
的 express
,然后我们继续找,到这
我们可以看到这个
setupApp
到此我们可以明确这个 devServer.app
就是一个 express
实例,所以在 OnBeforeSetupMiddleware
和 OnAfterSetupMiddleware
阶段我们可以注册自己的插件。
然后就有了第一版的 tokenMiddleware
,大概代码长下面那样
class TokenUtil {
constructor() {
this.token = ''
}
async refresh() {
// request token
this.token = 'xxxx'
}
}
createRefreshToken() {
const tokenUtil = new TokenUtil()
const LoginUtil = new LoginUtil()
return async (req, res, next) => {
// await login
// await token
// setHeader
}
}
class LoginUtil {
constructor() {
this.logined = false
}
async refresh() {
// request cookie
// cache cookie
}
}
webpack-dev-server
配置
module.exports = {
after: (ds) => {
ds.app.use(createRefreshToken())
}
}
然后经过调试之后发现我需要做很多额外的工作,判断是否是需要带 token
的请求之类的,做了很多额外操作,因为这些都只需要在 proxy
中做一下配置就好了,于是我就去翻了 proxy
文档。
webpack-dev-server proxy
然后对比了一下,发现有几个配置项是合适的,router
、onProxyRes
、onProxyReq
,经过尝试发现只有 router
符合要求,因为在 request
进来之后,我们需要做 token
获取的请求,在这我们需要把他 await
下来,onProxyRes
以及 onProxyReq
只是监听了对应的事件,并不能将请求 hold 下来,所以我们需要在 router
里面做完获取 cookie
以及获取 token
。很快我就把代码改完了,很快就发现 bug
住了,有一个库解析 protocol
的时候获取到的 protocol
是 undefined
,然后根据错误栈我找到了 http-proxy-middleware
的这
下图为 http-proxy-middleware
仓库代码。
在这会拿到当前的 proxy 配置项,然后我们看上面的 prepareProxyRequest
方法,然后我就在这打印,发现拿到的 target 配置项是一个 pending 的 Promise
,到这我们就发现情况不太对,明明文档上写的是可以传入异步函数,不死心,就进到 prepareProxyRequest
方法里面发现下面的结果
到这我们可以看到这确实是可以 await
的,难不成版本有问题?然后我找到了这个 issue
呀,果然用户有需求才有仓库的完善,然后我知道了这个异步的 router
到 2.x 版本才有。于是我去看了我们现在使用的版本
react-scripts
我们项目用的是 create-react-app
初始化的,没有 eject
所以很多东西都被隐藏了,于是我到 package.lcok.json
里面找到了当前使用的 http-proxy-middleware
版本,发现是 0.19.xx
的,现在问题就来了,升级很难,整个改动都很大。但是不升级,这个目前又解决不了,于是我去看了 react-scripts
,进去之后发现这个目录跟 eject
出来的目录很像
于是我就到 config
目录里面找到了我要的东西
我可以自定义 proxy middleware
(在这比较顺利是因为之前我有过 eject
react 脚手架的经验,所以找的会比较顺利),于是就有了下面的一个版本的 token middleware
module.exports = function (app) {
app.use('/prefix', createRefreshToken())
}
个人经验
平时多看,多 debug
,我之前将 create-react-app
eject
出来就是为了去 debug
这个脚手架的构造,以及学习他的结构。
遇到问题,要敢于深入到源码里面,将流程从你理解的代码层面来理解清楚,有时候文档可能不会写的很清楚,都是开源产品,有的作者可能精力不会有那么多来完善文档,这个时候就需要你自己去看作者代码,理解他,像庖丁解牛一样。