Koa下http代理
前言
最近做管理後臺的重構或者說重做. 至於為什麼要重構.
隨意的解釋:
- 是原來寫的人走了.
客觀的解釋:
- 用的人覺得不好用
- 維護的人員找不到北
再多一點解釋:
-
express + ejs的混合編寫
- 單獨抽象了Router層, 定義了controller層, service層, 但是強行繫結, 耦合很緊密
- 中介軟體過度使用, 本意想簡化操作, 反而程式設計負擔
- 服務端定義了渲染模板, 前端又有模板, 額
- 引入ts, 但是很多都是any, 介面很多都是直接把query或者body引數直接使用, 非常難跟蹤資料
- views未按照功能分資料夾, 全部在一個資料夾下, 頭暈的厲害
- 後臺配置不合理, 即使是選單這種配置也是完全一樣的幾份
-
前端雜亂
- jquery編寫, 本身並無問題, 沒有模組化, 出現程式碼多頁面混用, 一處修改, 可能多頁面出錯
- 體驗很差, 測試人員和開發人員都不一定能正常操作
額外說兩句, 這裡的express + ejs的專案屬於前端專案, 後臺有很多nodejs編寫的形式微服務的服務.
因為是後臺管理專案, 美觀要求並不是那麼高. 我的規劃是.
基於koa的node中間層
- 授權管理
- ACL管理, 這裡我們自己編寫的輕量級的許可權控制.
- 檔案上傳(阿里) 和其他可能需要定製的處理, 比如使用檔案批量上傳資料的處理
- 請求轉發
前端專案
使用 create-react-app + react-app-rewired + ant + mobx 構建專案,
ant design已經基本夠用, 實際上mobx都可以不用.
這裡就有兩個專案專案了, 一個ui專案, 一箇中間層api專案.
開發模式下, ui專案是通過dev-server啟動的, 會通過代理轉發請求到中間層api專案, 中間層api專案再轉發到實際的服務. 這一切看起來都很美好, 也沒毛病.
問題
我隨手拈來, 配置好, 開始請求. 就淚奔了. 請求死活過不去.
各種中介軟體嘗試
express下面有很好用的http-proxy-middleware, 但是koa並沒有, koa官方推薦的是koa-proxies 和koa-better-http-proxy, 自己搜尋發現 koa-proxy下載量和star都還要高一些, 於是自己就開始挨個試試, 均失敗.
開始懷疑是版本問題, 檢視均是支援的, 而且debug確實執行了請求傳送, debug進入原始碼發現 Socket hang up.
自己封裝
後來檢查原始碼, 其實都是基於http-proxy進行的封裝, 於是參考別人的程式碼, 自己簡單的封裝了一個版本, 進行debug, 結果依舊,
koa-connect
後來搜尋發現, koa下能使用express的中介軟體, 需要通過轉換, 這個中介軟體就是koa-connect, 於是進行切換, 結果還是失敗, 心疼
http-proxy 生命週期攔截
接著嘗試, 在http-proxy的各個生命週期進行攔截, 成效也不大, 倒是瞭解了一下http-proxy
x-www-form-urlencoded
我們的介面全部都是post呼叫的,而且接受的資料格式都是x-www-form-urlencoded, 偶爾一次發現, 使用get居然轉發到了伺服器, 只是提示不允許get呼叫, 其實說明已經能聯通, 但是post卻是過不去. 那就說明問題很可能處在資料傳遞的格式.
百度,bing和google搜尋
發現了這篇文章,
ofollow,noindex" target="_blank">http-proxy-middleware nodejs post請求超時問題 x-www-form-urlencoded
我把程式碼提前了, 結果真的是ok了, 我的眼淚啊.
但是, 不能這樣啊, 我的auth攔截肯定會先於proxy, auth之前肯定還有bodyParser, session等中介軟體, 大哥這可不行啊.
繼續搜尋edit-post-parameters-prior-to-forwarding-to-a-proxy-target-and-sending-response
onProxyReq(proxyReq, req, res) { if ( req.method == "POST" && req.body ) { // Add req.body logic here if needed.... // .... // Remove body-parser body object from the request if ( req.body ) delete req.body; // Make any needed POST parameter changes let body = new Object(); body.filename = 'reports/statistics/summary_2016.pdf'; body.routeid = 's003b012d002'; body.authid = 'bac02c1d-258a-4177-9da6-862580154960'; // URI encode JSON object body = Object.keys( body ).map(function( key ) { return encodeURIComponent( key ) + '=' + encodeURIComponent( body[ key ]) }).join('&'); // Update header proxyReq.setHeader( 'content-type', 'application/x-www-form-urlencoded' ); proxyReq.setHeader( 'content-length', body.length ); // Write out body changes to the proxyReq stream proxyReq.write( body ); proxyReq.end(); } }
看到重寫了content-type和content-length, 我就笑了. 還是自己太天真, 沒理解好這個onProxyReq方法, 於是我也這麼重寫, 再提前其他中介軟體, 就沒有問題了.
我真的就能苦笑了, 還好解決了問題. 關於http-proxy打算有時間深入看一看, 值得擁有.