Droplet——一款輕量的Golang應用層框架( 三 )


{"code": 0,// API 錯誤碼"message": "",// API 消息"data": {},// 響應數據"request_id": "" // 請求ID}當然你可以完全去掉這個默認 Wrapper 或者 使用滿足你們團隊規范的 Wrapper(需要實現 data.HttpResponse 接口) 來替換它:
type NativeJsonResp struct { data interface{}}func (n *NativeJsonResp) Set(code int, msg string, data interface{}) { n.data = https://www.huyubaike.com/biancheng/data}func (n *NativeJsonResp) SetReqID(reqId string) {}func (n *NativeJsonResp) MarshalJSON() ([]byte, error) { return json.Marshal(n.data)}func main() {... droplet.Option.ResponseNewFunc = func() data.HttpResponse {return &NativeJsonResp{} }...}對于另外一些并不需要 Wrapper 或者 你想要自行控制返回的內容時可以在 Handler 中使用一些實現了特定接口的返回值,如下所示:
func GetLoginQRCode(ctx droplet.Context) (interface{}, error) { type makeQRCodeResp struct {SceneID string `json:"scene_id"`Stateint`json:"state"`Urlstring `json:"url"` } var resp makeQRCodeResp if err := goreq.Get(UrlMakeQRCode, goreq.SetHeader(fakeClientHeader()), goreq.JsonResp(&resp)).Do(); err != nil {return nil, fmt.Errorf("get qrcode failed: %w", err) } return &data.RawResponse{StatusCode: http.StatusOK,Body:[]byte(fmt.Sprintf(QRCodeBase, resp.SceneID, resp.SceneID, resp.Url)), }, nil}類似的還有 data.FileResponse 、data.SpecCodeResponse , 根據其名字你可以在需要的場景選擇它們 。
同時在整形過程中,為了業務研發不再需要關心錯誤處理 , Droplet 會自動將 err != nil 的響應轉化到 code 與 message 字段上 。如下圖所示:
func ErrorAPI(ctx droplet.Context) (interface{}, error) {return nil, errors.New("failed")}那么你將得到如下的響應:
{"code": 10000,"message": "failed"}當然 , 你可以使用 data.BaseError 來指定你想返回的錯誤碼:
func ErrorAPI(ctx droplet.Context) (interface{}, error) {return nil, data.BaseError{Code: 100, Message: "custom message"}}

Tips
  • 這些特定的響應其背后都是實現了某一類接口,如果有需要你也完全可以自行實現 。
流量記錄Droplet 自帶了記錄 API 出參與入參的能力,但是默認所有記錄信息都會被拋棄,如果想要啟用它,你需要實現 Droplet 的全局 Logger,如下所示:
import ("github.com/shiningrush/droplet/log")func main() {...// CustomLogger 需要實現 log.Interface log.DefLogger = &CustomLogger{}// droplet 默認只會記錄 Path , Method,耗時等信息,如果你需要打印 API 的輸入與輸出,可以在全局選項中開啟(在Wraps函數中也可指定)droplet.Option.TrafficLogOpt = &middleware.TrafficLogOpt{LogReq:true,LogResp: true, }...}自定義中間件實現一個自定義中間件很簡單 , 你只需要實現與 Hanler 類似的接口即可,下圖是一個簡單的中間件,它會用于檢測輸入參數是否需要 Quota 并執行相關邏輯:
type DemoMiddleware struct {// 繼承基本的middleware,里面有用于實現處理鏈路的公共邏輯middleware.BaseMiddleware}func (mw *HttpInputMiddleware) Handle(ctx core.Context) error {if ck, ok := ctx.Input().(QuotaChecker); !ok {if err := ck.IsQuotaEnough(); err != nil {return err}}// 調用下一個中間件 , 有需要的話你也可以在響應返回后執行部分邏輯return mw.Handle(ctx)}func main() {// 如果你需要所有API都添加該中間件,可以在全局選項中將你的中間件編排 droplet.Option.Orchestrator = func(mws []core.Middleware) []core.Middleware {return append(mws, &DemoMiddleware{}) }...// 在單個API上啟用 r.POST("/json_input/:id", ginwrap.Wraps(APIHandler,wrapper.Orchestrator(func(mws []core.Middleware) []core.Middleware {return append(mws, &DemoMiddleware{}) })))...}
Tips
Q: 為什么使用 Orchestrator 這樣的形式來配置中間件,而非通過 Priorty 之類的權重來實現中間件的編排,這樣在未來可以做到通過配置文件來調整中間件
A: 主要出于幾個考慮
  1. 考慮現代微服務的架構下,多數業務無關的通用能力都會下沉到網關以及Mesh,因此一個服務的切面不會太多 , 在通過這樣的方式來配置,成本是可以接受的 。
  2. 通過 Orchestrator 方式,用戶還可以任意操作已添加的中間件,比如移除一些不必要的中間件,這是權重的方式無法做到的 。
  3. 當然如果以后有需要,現在的設計并不妨礙我們支持基于權重的方式
小結正如文中所說,Droplet 的核心目標是 提供位于應用層的、pipeline 形式的請求處理能力,并以此為基礎提供了一些開箱即用的中間件 。它對項目帶來的收益總結為幾點:

推薦閱讀