需求背景
最近接到一个URL改造的需求,想通过/a/b/edit/123的URL方式访问之前的/a/b/edit.html?id=123 页面。
本着修改最少的原则,我决定改一改网关转发规则,然后本地修改url上数据获取方法,是不是很机智?改完线上网关配置后,线上环境已经可以完美访问。但是突然发现本地调试还是得用/a/b/edit.html?id=123,/a/b/edit/123根本没法访问 orz 。
考虑到webpack proxy代理可能会处理这种情况,我随后在webpack官网上找到了bypass 。官网demo和我的需求简直一模一样,我的内心是狂喜的,以为ctrl+c/v就完事了(yeah!)。
开始解决
于是我对照着demo,写下了如下的代码:
运行后发现的确是ok的,通过/a/b/edit/的可以访问到本地的/a/b/edit.html 页面。但是当proxy配置增多的时候就会出现奇怪的事情。
上述配置,当我访问/a/b/edit/时,实际返回的居然是/a/c/view.html 页面。我????
bypass看起来return html后就完事了才对呀!为什么按顺序走到了下个context的bypass函数中?
查找原因
为了查找原因,走上了看源码的道路。
在webpack-dev-server的 class Server中可以找到对proxy的处理
代码流程如下:
- 对proxy对象进行了遍历,针对每一个proxy规则都注册了一个hanlde处理方法
- 在handle中判断bypass函数运行的结果,然后修改reques的url,从而实现对请求url的修改
- 最后将hanlde通过app.use的方式注册中间件,使得每一个访问都会进入到这个handle中处理
造成【请求/a/b/edit/,实际返回/a/c/view.html 页面】的原因是,我proxy中有2个代理配置,按上述代码流程,他们都会注册中间件,并且按照express洋葱模型来进行顺序处理。当/a/b/edit/请求过来后:
- 先进入了第一个
/a/b/edit/处理的hanlde中,这个时候req.url =/a/b/edit.html - 由于bypass的return并不会结束请求,所以请求会进入第二个handle,这个时候req.url =
/a/c/view.html - 结束请求,返回给前端的页面是
/a/c/view.html
好了,破案了!bypass的结果只是会在中间件中影响req的url属性,并且在中间件的处理过程中会按顺序来改变。所以我们需要在一个bypass中统一处理返回html的逻辑,或者在不满足条件的bypass中返回undefined,来避免req.url的链式修改。
最后方案
根据上述的分析,我们有两种方案来实现请求返回html的需求:
- 在一个bypass中统一处理返回html的逻辑
- 在多个proxy规则的bypass方法中进行判断,如果不满足匹配条件,需要返回undefined,来避免req.url的链式修改
笔者最终采用了第一种方案。在proxyList中保存需要转发html的url配置,然后在bypass中对所有的访问进行判断,如果满足proxy中path的要求,就返回相印的html页面。: )