最近在寫xmocker的工具,用於開發前期的mock數據,不可避免的用到了代理的中間件。當然,github上有關於http-proxy封裝的中間件。畢竟是自己練手的項目,就自己寫了個中間件,方便定制功能。
http-proxy庫用於koa中,是使用它的 proxy.web方法。常規的用法是:
proxy.web(req, res, { target: 'http://mytarget.com:8080' }, function(e) { ... });
項目中的要求是將API代理到用戶填寫的網址上去。Koa提供了req和res,用戶提供了網址,這樣中間件就很容易寫了
function proxyTo({ status }) { return async function (ctx, next) { return next().then(async () => { if (status === undefined || ctx.status === status) { let data try { data = await proxy.web(ctx.req, ctx.res) } catch (e) { if (err) console.log(err) return } } }) } }
設置訪問結果為404則進行代理,否則不進行代理。然后在koa的中間件中use這個函數就可以了。
基礎應用這樣自然就可以了,可是遇到POST的時候就不行了,因為用到了koa-bodyparser,導致post的請求沒法發給服務器。在http-proxy的issue中到處找解決辦法,終於找到了讓req重新stream的方法。
proxy.on('proxyReq', function (proxyReq, req, res, options) { if (req.body) { let bodyData = JSON.stringify(req.body) // incase if content-type is application/x-www-form-urlencoded -> we need to change to application/json proxyReq.setHeader('Content-Type', 'application/json') proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData)) // stream the content proxyReq.write(bodyData) } })
嗯,這樣基本就完成了一個Proxy的中間件。
剛開始用起來還不錯,至少普通的API都可以轉發了。於是試着代理到公司的API系統,網頁端也沒發現什么問題。然后用fiddler代理安卓客戶端,用正則代理鏈接至自己的服務,出現了亂碼。。。客戶端用的是okhttp發出的請求,fiddler上看轉發回來的json數據明明是正常的,結果在手機上中文就是不正確,都是十六進制代碼,根本沒解析。仔細對比:編碼?都是utf-8,這個沒問題。gzip?我明明是用的pipe過去,代碼原封不動啊。我試中捕獲了所有數據,用gzip解碼后,也是正確的,那為什么會出錯呢?
和別人交流了好久也沒找到問題。沒辦法,到代碼中找找。對於請求的header,發現cookie貌似少了一條,就看看header轉發時候怎么處理的。結果發現,header全是進行轉換的。issue中有提到set-cookie多條的問題,我這也是這個情況,嘗試着看看這個文件改動記錄,意外發現了一個Option項 :preserveHeaderKeyCase。
nodejs中header都是小寫的形式,所以http-proxy中就進行了轉換,將請求全部header轉為小寫。於是改了了true,這樣就不再轉換了。然后,客戶端正常了!!!哈哈,仔細一看,cookie還是不是多條。算了,先不管cookie了,至少普通登錄沒啥問題了。
另外加個changeOrigin為true可以防止有些服務器path取的是初始域名,出現404的問題。暫時就這么多啦。proxy插件的地址在:
https://github.com/wenlonghuo/xmocker/blob/master/app/plugin/proxy.js
歡迎試用我的Mock工具:
https://github.com/wenlonghuo/xmocker-cli