前後端分離應用的架構
在前後端分離架構中,爲了避免跨域以及暴露內部服務地址。一般來說,我會在Express這層中加入一個反向代理。
所有向後端服務訪問的請求,都通過代理轉發到內部的各個服務。
這個反向代理服務器,做起來很簡單。用http-proxy-middleware這個模塊,幾行代碼就可以搞定。
// app.js
Object.keys(proxyTable).forEach(function (context) {
app.use(proxyMiddleware(context, proxyTable[context]))
})
http-proxy-middleware實際上是對於node-http-proxy的更加簡便的封裝。node-http-proxy是http-proxy-middleware的底層包,如果node-http-proxy有問題,那麼這個問題就會影響到http-proxy-middleware這個包。
最近的bug
http-proxy-middleware最近有個問題,請求體在被代理轉發前,如果請求體被解析了。那麼後端服務將會收不到請求結束的消息,從瀏覽器的網絡面板可以看出,一個請求一直在pending狀態。
Cannot proxy after parsing body #299, 實際上這個問題在node-http-proxy也被提出過,而且處於open狀態。POST fails/hangs examples to restream also not working #1279
目前這個bug還是處於open狀態,但是還是有解決方案的。就是將請求體解析的中間件掛載在代理之後
。
下面的代碼,express.json()會對json格式的請求體進行解析。方案1在代理前就進行body解析,所有格式是json的請求體都會被解析。
但是有些走代理的請求,如果我們並不關心請求體的內容是什麼,實際上我們可以不解析那些走代理的請求。所以,可以先掛載代理中間件,然後掛載請求體解析中間件,最後掛載內部的一些接口服務。
// 方案1 bad
app.use(express.json())
Object.keys(proxyTable).forEach(function (context) {
app.use(proxyMiddleware(context, proxyTable[context]))
})
app.use('/api', (req, res, next)=> {
})
// 方案2 good
Object.keys(proxyTable).forEach(function (context) {
app.use(proxyMiddleware(context, proxyTable[context]))
})
app.use(express.json())
app.use('/api', (req, res, next)=> {
})
總結
經過這個問題,我對Express中間件的掛載順序有了更加深刻的認識。
同時,在使用第三方包的過程中,如果該包bug,那麼也需要自行找出合適的解決方案。而這個能力,往往就是高手與新手的區別。