背景
基于現在微服務或者服務化的思想,我們大部分的業務邏輯處理函數都是長這樣的:
比如grpc服務端:
grpc客戶端:
有些服務我們需要把它包裝為RESTful形式的接口,一般需要經歷以下步驟:
指定HTTP方法、URL
鑒權
參數綁定
處理請求
處理響應
可以發現,參數綁定、處理響應幾乎都是一樣模板代碼,鑒權也基本上是模板代碼(當然有些鑒權可能比較復雜)。
而Ginrest庫就是為了消除這些模板代碼,它不是一個復雜的框架,只是一個簡單的庫,輔助處理這些重復的事情,為了實現這個能力使用了Go1.18的泛型。
特性
這個庫提供以下特性:
封裝RESTful請求響應
封裝RESTful請求為標準格式服務
封裝標準格式服務處理結果為標準RESTful響應格式:Rsp{code, msg, data}
默認使用統一數字錯誤碼格式:[0, 4XXXX, 5XXXX]
默認使用標準錯誤格式:Error{code, msg}
默認統一狀態碼[200, 400, 500]
提供Recovery中間件,統一panic時的響應格式
提供SetKey()、GetKey()方法,用于存儲請求上下文(泛型)
提供ReqFunc(),用于設置Req(泛型)
使用例子
首先我們實現兩個簡單的服務:
然后使用Gin+Ginrest包裝為RESTful接口:
可以看到Register()里面每個接口都只需要一行代碼!
運行上面代碼,然后嘗試訪問接口,可以看到返回結果:
實現原理
Do()和DoOpt()都會轉發到do(),它其實是一個模板函數,把臟活累活給處理了:
功能列表
處理請求
用于把一個標準服務封裝為一個RESTfulgin.HandlerFunc,對應Do()、DoOpt()函數。
DoOpt()相比于Do()多了一個opts參數,因為很多rpc框架客戶端都有一個opts參數作為結尾。
還有一個BindJSON(),用于把請求體包裝為一個Req結構體:
如果無法使用Do()和DoOpt()則可以使用此方法。
處理響應
用于把rsp、error、errcode、errmsg等數據封裝為一個JSON格式響應體,對應ProcessRsp()、Success()、Failure()、FailureCodeMsg()函數。
比如ProcessRsp()需要帶上rsp和error,這樣業務里面就不需要再寫如下模板代碼了:
響應格式統一為:
Success()用于處理成功情況:
其余同理。
如果無法使用Do()和DoOpt()則可以使用這些方法。
處理錯誤
一般我們都需要在出錯時帶上一個業務錯誤碼,方便客戶端處理。因此我們需要提供一個合適的error類型:
我們提供了一些函數方便使用Error,對應NewError()、ToError()、ErrCode()、ErrMsg()、ErrEqual()函數。
比如NewError()生成一個Error類型error:
請求上下文操作
Gin的請求是鏈式處理的,也就是多個handler順序的處理一個請求,比如:
這個接口經歷了Verify和ginrest.Do兩個handler,其中我們在Verify的時候通過認證知道了用戶的身份信息(比如uid),我們希望把這個uid存起來,這樣可以在業務邏輯里使用。
因此我們提供了SetKey()、GetKey()兩個函數,用于存儲請求上下文:
比如認證通過后我們可以設置UID到上下文,然后在reqFunc()里讀取設置到req里面(下面介紹)。
請求結構體處理
上面我們設置了請求上下文,比如UID,但是其實我們并不知道具體這個UID是需要設置到req里的哪個字段,因此我們提供了一個回調函數ReqFunc(),用于設置Req:
注
如果這個庫的設計不符合具體的業務,也可以按照這種思路去封裝一個類似的庫,只要盡可能的統一請求、響應的格式,就可以減少很多重復的模板代碼。