Koa下http代理

前言

最近做管理後臺的重構或者說重做. 至於爲什麼要重構.
隨意的解釋:

  1. 是原來寫的人走了.

客觀的解釋:

  1. 用的人覺得不好用
  2. 維護的人員找不到北

再多一點解釋:

  1. express + ejs的混合編寫
    • 單獨抽象了Router層, 定義了controller層, service層, 但是強行綁定, 耦合很緊密
    • 中間件過度使用, 本意想簡化操作, 反而編程負擔
    • 服務端定義了渲染模板, 前端又有模板, 額
    • 引入ts, 但是很多都是any, 接口很多都是直接把query或者body參數直接使用, 非常難跟蹤數據
    • views未按照功能分文件夾, 全部在一個文件夾下, 頭暈的厲害
    • 後臺配置不合理, 即使是菜單這種配置也是完全一樣的幾份
  2. 前端雜亂
    • jquery編寫, 本身並無問題, 沒有模塊化, 出現代碼多頁面混用, 一處修改, 可能多頁面出錯
    • 體驗很差, 測試人員和開發人員都不一定能正常操作

額外說兩句, 這裏的express + ejs的項目屬於前端項目, 後臺有很多nodejs編寫的形式微服務的服務.

因爲是後臺管理項目, 美觀要求並不是那麼高. 我的規劃是.

基於koa的node中間層

  1. 授權管理
  2. ACL管理, 這裏我們自己編寫的輕量級的權限控制.
  3. 文件上傳(阿里) 和其他可能需要定製的處理, 比如使用文件批量上傳數據的處理
  4. 請求轉發

前端項目
使用 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搜索

發現了這篇文章,
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打算有時間深入看一看, 值得擁有.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章