99精品伊人亚洲|最近国产中文炮友|九草在线视频支援|AV网站大全最新|美女黄片免费观看|国产精品资源视频|精彩无码视频一区|91大神在线后入|伊人终合在线播放|久草综合久久中文

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

包裝RESTful形式的接口步驟

馬哥Linux運維 ? 來源:馬哥Linux運維 ? 作者:馬哥Linux運維 ? 2022-09-22 09:52 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

背景

基于現(xiàn)在微服務(wù)或者服務(wù)化的思想,我們大部分的業(yè)務(wù)邏輯處理函數(shù)都是長這樣的:

比如grpc服務(wù)端:

func (s *Service) GetUserInfo(ctx context.Context, req *pb.GetUserInfoReq) (*pb.GetUserInfoRsp, error) {    // 業(yè)務(wù)邏輯    // ...}

grpc客戶端:

func (s *Service) GetUserInfo(ctx context.Context, req *pb.GetUserInfoReq, opts ...grpc.CallOption) (*pb.GetUserInfoRsp, error) {    // 業(yè)務(wù)邏輯    // ...}

有些服務(wù)我們需要把它包裝為RESTful形式的接口,一般需要經(jīng)歷以下步驟:

指定HTTP方法、URL

鑒權(quán)

參數(shù)綁定

處理請求

處理響應(yīng)

可以發(fā)現(xiàn),參數(shù)綁定、處理響應(yīng)幾乎都是一樣模板代碼,鑒權(quán)也基本上是模板代碼(當然有些鑒權(quán)可能比較復雜)。

而Ginrest庫就是為了消除這些模板代碼,它不是一個復雜的框架,只是一個簡單的庫,輔助處理這些重復的事情,為了實現(xiàn)這個能力使用了Go1.18的泛型。

倉庫地址:https://github.com/jiaxwu/ginrest

特性

這個庫提供以下特性:

封裝RESTful請求響應(yīng)

封裝RESTful請求為標準格式服務(wù)

封裝標準格式服務(wù)處理結(jié)果為標準RESTful響應(yīng)格式:Rsp{code, msg, data}

默認使用統(tǒng)一數(shù)字錯誤碼格式:[0, 4XXXX, 5XXXX]

默認使用標準錯誤格式:Error{code, msg}

默認統(tǒng)一狀態(tài)碼[200, 400, 500]

提供Recovery中間件,統(tǒng)一panic時的響應(yīng)格式

提供SetKey()、GetKey()方法,用于存儲請求上下文(泛型)

提供ReqFunc(),用于設(shè)置Req(泛型)

使用例子

示例代碼在:https://github.com/jiaxwu/ginrest/blob/main/examples/main.go

首先我們實現(xiàn)兩個簡單的服務(wù):

const ( ErrCodeUserNotExists = 40100 // 用戶不存在)
type GetUserInfoReq struct { UID int `json:"uid"`}
type GetUserInfoRsp struct { UID      int    `json:"uid"` Username string `json:"username"` Age      int    `json:"age"`}
func GetUserInfo(ctx context.Context, req *GetUserInfoReq) (*GetUserInfoRsp, error) { if req.UID != 10 {  return nil, ginrest.NewError(ErrCodeUserNotExists, "user not exists") } return &GetUserInfoRsp{  UID:      req.UID,  Username: "user_10",  Age:      10, }, nil}
type UpdateUserInfoReq struct { UID      int    `json:"uid"` Username string `json:"username"` Age      int    `json:"age"`}
type UpdateUserInfoRsp struct{}
func UpdateUserInfo(ctx context.Context, req *UpdateUserInfoReq) (*UpdateUserInfoRsp, error) { if req.UID != 10 {  return nil, ginrest.NewError(ErrCodeUserNotExists, "user not exists") } return &UpdateUserInfoRsp{}, nil}

然后使用Gin+Ginrest包裝為RESTful接口:

可以看到Register()里面每個接口都只需要一行代碼!

func main() { e := gin.New() e.Use(ginrest.Recovery()) Register(e) if err := e.Run("127.0.0.1:8000"); err != nil {  log.Println(err) }}
// 注冊請求func Register(e *gin.Engine) { // 簡單請求,不需要認證 e.GET("/user/info/get", ginrest.Do(nil, GetUserInfo)) // 認證,綁定UID,處理        reqFunc := func(c *gin.Context, req *UpdateUserInfoReq) {  req.UID = GetUID(c) } // 這里拆多一步是為了顯示第一個參數(shù)是ReqFunc e.POST("/user/info/update", Verify, ginrest.Do(reqFunc, UpdateUserInfo))}
const ( KeyUserID = "KeyUserID")
// 簡單包裝方便使用func GetUID(c *gin.Context) int { return ginrest.GetKey[int](c, KeyUserID)}
// 簡單包裝方便使用func SetUID(c *gin.Context, uid int) { ginrest.SetKey(c, KeyUserID, uid)}
// 認證func Verify(c *gin.Context) { // 認證處理 // ...        // 忽略認證的具體邏輯 SetUID(c, 10)}

運行上面代碼,然后嘗試訪問接口,可以看到返回結(jié)果:

請求1GET http://127.0.0.1:8000/user/info/get{    "uid": 10}響應(yīng)1{    "code": 0,    "msg": "ok",    "data": {        "uid": 10,        "username": "user_10",        "age": 10    }}
請求2GET http://127.0.0.1:8000/user/info/get{    "uid": 1}響應(yīng)2{    "code": 40100,    "msg": "user not exists"}
請求3POST http://127.0.0.1:8000/user/info/update{    "username": "jiaxwu",    "age": 10}響應(yīng)3{    "code": 0,    "msg": "ok",    "data": {}}

實現(xiàn)原理

Do()和DoOpt()都會轉(zhuǎn)發(fā)到do(),它其實是一個模板函數(shù),把臟活累活給處理了:

// 處理請求func do[Req any, Rsp any, Opt any](reqFunc ReqFunc[Req], serviceFunc ServiceFunc[Req, Rsp], serviceOptFunc ServiceOptFunc[Req, Rsp, Opt], opts ...Opt) gin.HandlerFunc { return func(c *gin.Context) {  // 參數(shù)綁定  req, err := BindJSON[Req](c)  if err != nil {   return  }  // 進一步處理請求結(jié)構(gòu)體  if reqFunc != nil {   reqFunc(c, req)  }  var rsp *Rsp  // 業(yè)務(wù)邏輯函數(shù)調(diào)用  if serviceFunc != nil {   rsp, err = serviceFunc(c, req)  } else if serviceOptFunc != nil {   rsp, err = serviceOptFunc(c, req, opts...)  } else {   panic("must one of ServiceFunc and ServiceFuncOpt")  }  // 處理響應(yīng)  ProcessRsp(c, rsp, err) }}

功能列表

處理請求

用于把一個標準服務(wù)封裝為一個RESTfulgin.HandlerFunc,對應(yīng)Do()、DoOpt()函數(shù)。

DoOpt()相比于Do()多了一個opts參數(shù),因為很多rpc框架客戶端都有一個opts參數(shù)作為結(jié)尾。

還有一個BindJSON(),用于把請求體包裝為一個Req結(jié)構(gòu)體:

// 參數(shù)綁定func BindJSON[T any](c *gin.Context) (*T, error) { var req T if err := c.ShouldBindJSON(&req); err != nil {  FailureCodeMsg(c, ErrCodeInvalidReq, "invalid param")  return nil, err } return &req, nil}

如果無法使用Do()和DoOpt()則可以使用此方法。

處理響應(yīng)

用于把rsp、error、errcode、errmsg等數(shù)據(jù)封裝為一個JSON格式響應(yīng)體,對應(yīng)ProcessRsp()、Success()、Failure()、FailureCodeMsg()函數(shù)。

比如ProcessRsp()需要帶上rsp和error,這樣業(yè)務(wù)里面就不需要再寫如下模板代碼了:

// 處理簡單響應(yīng)func ProcessRsp(c *gin.Context, rsp any, err error) { if err != nil {  Failure(c, err)  return } Success(c, rsp)}

響應(yīng)格式統(tǒng)一為:

// 響應(yīng)type Rsp struct { Code int    `json:"code"` Msg  string `json:"msg"` Data any    `json:"data,omitempty"`}

Success()用于處理成功情況:

// 請求成功func Success(c *gin.Context, data any) { ginRsp(c, http.StatusOK, &Rsp{  Code: ErrCodeOK,  Msg:  "ok",  Data: data, })}

其余同理。

如果無法使用Do()和DoOpt()則可以使用這些方法。

處理錯誤

一般我們都需要在出錯時帶上一個業(yè)務(wù)錯誤碼,方便客戶端處理。因此我們需要提供一個合適的error類型:

// 錯誤type Error struct { Code int    `json:"code"` Msg  string `json:"msg"`}

我們提供了一些函數(shù)方便使用Error,對應(yīng)NewError()、ToError()、ErrCode()、ErrMsg()、ErrEqual()函數(shù)。

比如NewError()生成一個Error類型error:

// 通過code和msg產(chǎn)生一個錯誤func NewError(code int, msg string) error { return &Error{  Code: code,  Msg:  msg, }}

請求上下文操作

Gin的請求是鏈式處理的,也就是多個handler順序的處理一個請求,比如:

        reqFunc := func(c *gin.Context, req *UpdateUserInfoReq) {  req.UID = ginrest.GetKey[int](c, KeyUserID) }        // 認證,綁定UID,處理 e.POST("/user/info/update", Verify, ginrest.Do(reqFunc, UpdateUserInfo))

這個接口經(jīng)歷了Verify和ginrest.Do兩個handler,其中我們在Verify的時候通過認證知道了用戶的身份信息(比如uid),我們希望把這個uid存起來,這樣可以在業(yè)務(wù)邏輯里使用。

因此我們提供了SetKey()、GetKey()兩個函數(shù),用于存儲請求上下文:

比如認證通過后我們可以設(shè)置UID到上下文,然后在reqFunc()里讀取設(shè)置到req里面(下面介紹)。

// 認證func Verify(c *gin.Context) { // 認證處理 // ... // 忽略認證的具體邏輯 ginrest.SetKey(c, KeyUserID, uid)}

請求結(jié)構(gòu)體處理

上面我們設(shè)置了請求上下文,比如UID,但是其實我們并不知道具體這個UID是需要設(shè)置到req里的哪個字段,因此我們提供了一個回調(diào)函數(shù)ReqFunc(),用于設(shè)置Req:

 // 這里↓        reqFunc := func(c *gin.Context, req *UpdateUserInfoReq) {  req.UID = ginrest.GetKey[int](c, KeyUserID) }        // 認證,綁定UID,處理 e.POST("/user/info/update", Verify, ginrest.Do(reqFunc, UpdateUserInfo))

如果這個庫的設(shè)計不符合具體的業(yè)務(wù),也可以按照這種思路去封裝一個類似的庫,只要盡可能的統(tǒng)一請求、響應(yīng)的格式,就可以減少很多重復的模板代碼。

審核編輯:彭靜
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    9005

    瀏覽量

    153770
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4381

    瀏覽量

    64906
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4900

    瀏覽量

    70758

原文標題:一行代碼實現(xiàn)一個 RESTful 接口

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    請問LabVIEW通過無線WiFi采集數(shù)據(jù) 基于restful

    本帖最后由 eehome 于 2013-1-5 09:47 編輯 求助,有沒有兄弟做過相關(guān)的項目啊,labview編寫一個程序作為一個web service,基于restful。 需要將采集到
    發(fā)表于 03-22 18:08

    編寫restful

    求助,有沒有兄弟做過相關(guān)的項目啊,現(xiàn)在寫了一個程序作為一個web service,基于restful。 需要將采集到的數(shù)據(jù)經(jīng)過wifi傳輸,數(shù)據(jù)傳輸想用到restful。有沒有推薦的自帶的restful的wifi模塊,或者有沒有
    發(fā)表于 03-22 18:12

    ISTA 國際包裝運輸組織---電子產(chǎn)品包裝運輸考察

    ISTA 1A 測試標準中的步驟及內(nèi)容1A測試步驟 固定放置振動測試 1、將包裝件3面朝下放在振動臺上 2、開啟振動系統(tǒng),使儀器以最低頻率振動且振幅為1英寸 3、保持此振幅,同時逐漸增大振動頻率
    發(fā)表于 08-04 17:37

    restful api設(shè)計規(guī)范

    清晰、符合標準、易于理解以及擴展方便等特點,受到越來越多網(wǎng)站的采用!Restful API接口規(guī)范包括以下部分:一、協(xié)議API與用戶的通信協(xié)議,總是使用HTTPs協(xié)議。二、域名應(yīng)該盡量將API部署在
    發(fā)表于 03-26 16:26

    python restful api學習技巧精選2

    python restful api 學習筆記2 快速開始
    發(fā)表于 09-16 13:39

    一文知道后端接口開發(fā)json,jsonp,restful

    json、jsonp/** * 后臺接口開發(fā) * json接口 * jsonp接口(解決跨域問題) * restful接口 */const
    發(fā)表于 11-04 07:22

    什么是restful以及restfulAPI的設(shè)計風格?

    如何理解restful架構(gòu)?什么是restful API ? restful API的設(shè)計風格和序列化?restful API之請求與響應(yīng) ?
    發(fā)表于 11-04 08:25

    為什么需要接口?接口電路有哪些形式

    什么是接口?為什么需要接口接口硬件包含哪些部分?接口軟件有什么功能?接口電路有哪些形式?什么是
    發(fā)表于 12-23 07:27

    在4.3.1版的restful_server示例中找不到npm是怎么回事?

    從 Eclipse 輸出:CMake Error at main/CMakeLists.txt:10 (message):C:/esp-431/ws431/restful_server/main
    發(fā)表于 02-17 07:31

    車載逆變電源包裝

    車載逆變電源包裝              包裝主要是指車載應(yīng)急電源在運輸、銷售過程中的包裝
    發(fā)表于 01-04 13:42 ?610次閱讀

    Constrained RESTful Environments (CoRE) Link Format

    Constrained RESTful Environments (CoRE) link Format,受限的RESTful環(huán)境鏈路格式
    發(fā)表于 11-26 15:23 ?6次下載

    Restful 和 RPC 是什么關(guān)系與區(qū)別

    本文詳細介紹了關(guān)于Restful 和 RPC的關(guān)系與區(qū)別,詳細分析請看下文。
    的頭像 發(fā)表于 02-07 15:35 ?3.9w次閱讀
    <b class='flag-5'>Restful</b> 和 RPC 是什么關(guān)系與區(qū)別

    彈簧包裝機換包裝薄膜步驟的簡單介紹

    包裝機來為她們提升工作效能。 彈簧包裝機在顆粒物包裝領(lǐng)域公司的應(yīng)用全過程中,許多都不清楚彈簧包裝機換包裝薄膜的步驟。今日我為大伙兒解讀一下彈
    發(fā)表于 02-19 16:28 ?2348次閱讀

    構(gòu)建RESTful Web服務(wù)的過程

    本指南將引導您完成使用 Spring 創(chuàng)建“Hello, World”RESTful Web 服務(wù)的過程。
    的頭像 發(fā)表于 09-06 15:47 ?886次閱讀

    使用RESTful Web服務(wù)的過程

    本指南將引導您完成創(chuàng)建使用#spring# #spring認證# RESTful Web 服務(wù)的應(yīng)用程序的過程。
    的頭像 發(fā)表于 09-06 15:47 ?892次閱讀