重復造輪子系列——基於Ocelot實現類似支付寶接口模式的網關


重復造輪子系列——基於Ocelot實現類似支付寶接口模式的網關

引言

重復造輪子系列是自己平時的一些總結。有的輪子依賴社區提供的輪子為基礎,這里把使用過程的一些覺得有意思的做個分享。有些思路或者方法在大神看來可能會比較low。但是能解決實際問題,相信有需要的人也在尋找類似的解決方案。這里可以算作是提供了一種思路,類似問題如果有讀者能有更好的解決方案,願聞其詳。

若有閱讀后引起內心沖突或者憤怒等不適以及自覺被誤導者,不需要切換到抖音等歡樂頻道進行綜合調理,直接就可以在評論區吐槽。

 

網關簡介

什么是網關,為什么用網關。這些問題網上有很多文章,講解的非常全面。這里就不做重復的講解了。

但后面的內容至少需要了解網關下面兩點。

API網關是一個服務器,是系統的唯一入口。

API網關方式的核心要點是,所有的客戶端和消費端都通過統一的網關接入微服務,在網關層處理所有的非業務功能(提供監控、鑒權、負載均衡等)。

 

 

 

默認實現

下面演示的項目使用vs2019,Asp.Net Core 2.1開發

1、創建一個ASP.NET Core API項目Agile.Demo1.API,使用Swagger作為在線UI展示

項目結構如圖1

 

圖1

 

發布並且運行,為了方面啟動運行,寫了個批處理腳本,如圖2

 

圖2

 

直接雙擊start運行如圖3

 

圖3

 

瀏覽器打開顯示效果如圖4

 

圖4

 

直接Swagger文檔在線測試各個接口正常。

 

 

2、創建一個ASP.NET Core API項目Agile.Demo2.API 與Agile.Demo1.API項目類似。

 

 

3、創建一個基於ocelot的網關服務,項目結構如圖5

 

圖5

 

這里使用Ocelot來做網關,Ocelot是一堆特定順序的中間件

 

配置ocelot.json,配置內容如下

{

  "ReRoutes": [

    //API01 業務接口1

    {

      "DownstreamPathTemplate": "/{url}",

      "DownstreamScheme": "http",

      "DownstreamHostAndPorts": [

        {

          "Host": "127.0.0.1",

          "Port": 9001

        }

      ],

      "UpstreamPathTemplate": "/demo1/{url}",

      "UpstreamHttpMethod": [ "Post", "Get" ],

      "ReRoutesCaseSensitive": false

    },

    //API02 業務接口2

    {

      "DownstreamPathTemplate": "/{url}",

      "DownstreamScheme": "http",

      "DownstreamHostAndPorts": [

        {

          "Host": "127.0.0.1",

          "Port": 9002

        }

      ],

      "UpstreamPathTemplate": "/demo2/{url}",

      "UpstreamHttpMethod": [ "Post", "Get" ],

      "ReRoutesCaseSensitive": false

    }

  ]

}

 

 

這個配置比較簡單,就配置了兩個下游的業務接口。

把兩個業務接口站點和網關站點都運行起來,如圖6

 

圖6

 

使用postman直接測試demo1 里面的 saveorder接口,如圖7

 

圖7

 

使用postman直接測試demo2 里面的 saveorder接口,如圖8

 

圖8

 

使用postman通過網關訪問demo1,如圖9

 

圖9

 

能正常返回數據,說明網關的轉發正常。

通過網關訪問demo2也類似,這里就不截圖了。下面提供demo代碼可以下載自己測試下。

 

這里只介紹,通過網關的轉發,其他網關方面的更多應用不在這里做介紹。

 

 

 

新的問題

有一次,我們提供接口和其他部門對接。按照慣例把接口以及網關部署好,文檔提供,讓他們按照文檔規定的傳就可以了。

結果,他們看了文檔后提出了疑問,這是什么網關。每個接口請求地址還得拼接出來作為完整的請求,我們代碼要做很多調整啊。能不能做成支付寶那種,就一個地址固定不變,然后公共參數,業務參數封裝的模式。因為這種模式封裝的東西都有現成的,這樣我們就不用很大的改動就可以快速對接了。看下支付寶接口,如圖10

 

 

圖10

 

我想你這公共參數還不是動態的,相當於原來我們提供的網關地址后面加的就是對應的動態數據,道理都一樣的啊,但受阿里系影響,他們接口的開發還是對接都是習慣按照支付寶這種模式來的,封裝的公共參數什么的都做好了,要調整很麻煩。接口不按照他們的樣子來就別扭,增加他們工作量。

當時我想這怎么辦,我出接口應該按照我們的要求來啊,但沒辦法不夠強勢,還得按照他們阿里系規則來,那就想辦法吧。

想到ocelot也是一系列的中間件處理 的,我想那就增加一個中間件,把請求給攔截了,重新組合數據,再下發。

這樣可以保證我們內部的調用不變,對外兼容這種請求方式。說干就干,先做個demo試驗下能否行得通。

增加一個中間件GatewayMiddleware,代碼如下,既然要按照支付寶接口的來,那干脆把公共參數這塊整體搬過來。

    

// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project

    public class GatewayMiddleware { private readonly RequestDelegate _next; public GatewayMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext httpContext) { //支持類似支付寶的網關模式 //公共請求參數 //app_id method format charset sign_type sign timestamp version app_auth_token biz_content //請求參數(業務),建議json格式 //trade_no out_trade_no operator_id

            if (httpContext.Request.Path.ToString().ToLower() == "/gateway"|| httpContext.Request.Path.ToString().ToLower() == "/gateway.do") { //調用方post form提交,獲取公共請求參數,處理做轉發

                if (httpContext.Request.Method.ToLower() == "get") { httpContext.Response.ContentType = "text/plain;charset=utf-8"; await httpContext.Response.WriteAsync("調試錯誤,請回到請求來源地,重新發起請求"); return; } var reqForm = httpContext.Request.Form; if (reqForm == null || reqForm.Count == 0) { httpContext.Response.ContentType = "text/plain;charset=utf-8"; await httpContext.Response.WriteAsync("調試錯誤,請回到請求來源地,重新發起請求"); return; } var app_id = reqForm.ContainsKey("appid") ? reqForm["appid"].ToString() : ""; var method = reqForm.ContainsKey("method") ? reqForm["method"].ToString() : "";//接口名稱(格式:模塊.控制器.方法) 比如demo1.Values.SaveOrder

                var format = reqForm.ContainsKey("format") ? reqForm["format"].ToString() : "json"; var charset = reqForm.ContainsKey("charset") ? reqForm["charset"].ToString() : "utf-8"; var sign_type = reqForm.ContainsKey("sign_type") ? reqForm["sign_type"].ToString() : "md5"; var sign = reqForm.ContainsKey("sign") ? reqForm["sign"].ToString() : ""; var timestamp = reqForm.ContainsKey("timestamp") ? reqForm["timestamp"].ToString() : ""; var version = reqForm.ContainsKey("version") ? reqForm["version"].ToString() : ""; var app_auth_token = reqForm.ContainsKey("app_auth_token") ? reqForm["app_auth_token"].ToString() : ""; var biz_content = reqForm.ContainsKey("biz_content") ? reqForm["biz_content"].ToString() : "";//業務接口參數 json格式 //通過method參數拆分出 模塊 控制器 方法

                var methods = method.Split('.'); var moduleName = method.Length > 0 ? methods[0] : ""; var controllerName = method.Length > 1 ? methods[1] : ""; var actionName = method.Length > 2 ? methods[2] : ""; //區分有版本和無版本兩種情況,version不傳或傳空就是無版本

                var nextPath = string.IsNullOrEmpty(version) ? $"/{moduleName}/api/{controllerName}/{actionName}" : $"/{moduleName}/api/v{version}/{controllerName}/{actionName}"; //下游業務接口暫時只支持post json格式的請求

                byte[] postData = Encoding.GetEncoding(charset).GetBytes(biz_content); httpContext.Request.Path = nextPath; httpContext.Request.ContentType = "application/json"; httpContext.Request.ContentLength = postData.Length; httpContext.Request.Body = new MemoryStream(postData); await _next(httpContext); } else { await _next(httpContext); } } } // Extension method used to add the middleware to the HTTP request pipeline.

    public static class GatewayMiddlewareExtensions { public static void UseGatewayMiddleware(this IApplicationBuilder app) { app.UseMiddleware<GatewayMiddleware>(); } }

 

 

 

Startup.cs增加如下代碼,如圖11

 

測試攔截成功,重新組裝下發。能夠正常返回,測試成功。

 

具體的操作看代碼的說明,這里就不再贅述。

 

這里有一點要特別說明。因為公共參數是form表單post提交,所以調用方請求過來肯定是post方式。轉到下游的時候這個請求類型沒有改變,所有暫時只支持下游是post的接口。不過可以增加個參數或者使用format參數值來做區分下游具體是get還是post。因為現在format是json肯定只能是支持post。

 

測試聯調

以訪問demo1為例,這里有三種方式訪問demo1,使用postman測試如下

1、直接訪問,如上面圖7

2、通過網關轉發方式1,如上面圖9

3、通過網關轉發方式2,如圖12

 

圖12

 

使用這種方式還是有優勢的,比如參數簽名這塊就可以從業務里面獨立出來,在網關處理了。

 

 

總結

說服不了,就多干活,多想方案。

 

由於一個文件最大10M,這里拆開上傳

APIDemo1代碼下載,APIDemo2代碼下載,APIGateway代碼下載

 

 

感謝閱讀,希望這篇文章能給你帶來幫助!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM