亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Go?Ginrest實現(xiàn)一個RESTful接口

 更新時間:2022年08月18日 09:56:15   作者:jiaxwu  
這篇文章主要為大家介紹了Go?Ginrest實現(xiàn)一個RESTful接口示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

背景

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

比如grpc服務端:

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

grpc客戶端:

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

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

  • 指定HTTP方法、URL
  • 鑒權
  • 參數(shù)綁定
  • 處理請求
  • 處理響應

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

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

倉庫地址:github.com/jiaxwu/ginr…

特性

這個庫提供以下特性:

  • 封裝RESTful請求響應
    • 封裝RESTful請求為標準格式服務
    • 封裝標準格式服務處理結果為標準RESTful響應格式:Rsp{code, msg, data}
    • 默認使用統(tǒng)一數(shù)字錯誤碼格式:[0, 4XXXX, 5XXXX]
    • 默認使用標準錯誤格式:Error{code, msg}
    • 默認統(tǒng)一狀態(tài)碼[200, 400, 500]
  • 提供Recovery中間件,統(tǒng)一panic時的響應格式
  • 提供SetKey()、GetKey()方法,用于存儲請求上下文(泛型)
  • 提供ReqFunc(),用于設置Req(泛型)

使用例子

示例代碼在:github.com/jiaxwu/ginr…

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

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)
}

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

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

實現(xiàn)原理

Do()和DoOpt()都會轉發(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
		}
		// 進一步處理請求結構體
		if reqFunc != nil {
			reqFunc(c, req)
		}
		var rsp *Rsp
		// 業(yè)務邏輯函數(shù)調用
		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")
		}
		// 處理響應
		ProcessRsp(c, rsp, err)
	}
}

功能列表

處理請求

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

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

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

// 參數(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()則可以使用此方法。

處理響應

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

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

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

響應格式統(tǒ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è)務錯誤碼,方便客戶端處理。因此我們需要提供一個合適的error類型:

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

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

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

// 通過code和msg產生一個錯誤
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))

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

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

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

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

請求結構體處理

上面我們設置了請求上下文,比如UID,但是其實我們并不知道具體這個UID是需要設置到req里的哪個字段,因此我們提供了一個回調函數(shù)ReqFunc(),用于設置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))

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

以上就是Go Ginrest實現(xiàn)一個RESTful接口的詳細內容,更多關于Go Ginrest實現(xiàn)RESTful接口的資料請關注腳本之家其它相關文章!

相關文章

  • golang 后臺進程的啟動和停止操作

    golang 后臺進程的啟動和停止操作

    這篇文章主要介紹了golang 后臺進程的啟動和停止操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • golang實現(xiàn)并發(fā)數(shù)控制的方法

    golang實現(xiàn)并發(fā)數(shù)控制的方法

    下面小編就為大家分享一篇golang實現(xiàn)并發(fā)數(shù)控制的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • Golang?使用os?庫的?ReadFile()?讀文件最佳實踐

    Golang?使用os?庫的?ReadFile()?讀文件最佳實踐

    這篇文章主要介紹了Golang使用os庫的ReadFile()讀文件最佳實踐,文章圍繞主題展開詳細的內容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-09-09
  • Golang中map數(shù)據(jù)類型的使用方法

    Golang中map數(shù)據(jù)類型的使用方法

    這篇文章主要介紹了Golang中map數(shù)據(jù)類型的使用方法,文章圍繞主題展開詳細的內容戒殺,具有一定的參考價值,需要的朋友可以參考一下
    2022-09-09
  • 淺析Go語言中閉包的定義與使用

    淺析Go語言中閉包的定義與使用

    閉包是編程語言中的一個重要概念,它允許函數(shù)不僅僅是獨立的代碼塊,還可以攜帶數(shù)據(jù)和狀態(tài),本文將深入探討閉包的定義、用途和注意事項,以及如何正確使用閉包,有需要的可以參考下
    2023-09-09
  • golang結合mysql設置最大連接數(shù)和最大空閑連接數(shù)

    golang結合mysql設置最大連接數(shù)和最大空閑連接數(shù)

    本文介紹golang?中連接MySQL時,如何設置最大連接數(shù)和最大空閑連接數(shù),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • Go實現(xiàn)比較時間大小

    Go實現(xiàn)比較時間大小

    這篇文章主要介紹了Go實現(xiàn)比較時間大小的方法和示例,非常的簡單實用,有需要的小伙伴可以參考下。
    2015-04-04
  • Go語言操作MySQL的知識總結

    Go語言操作MySQL的知識總結

    Go語言中的database/sql包提供了保證SQL或類SQL數(shù)據(jù)庫的泛用接口,并不提供具體的數(shù)據(jù)庫驅動。本文介紹了Go語言操作MySQL的相關知識,感興趣的可以了解一下
    2022-11-11
  • Go實現(xiàn)分布式系統(tǒng)高可用限流器實戰(zhàn)

    Go實現(xiàn)分布式系統(tǒng)高可用限流器實戰(zhàn)

    這篇文章主要為大家介紹了Go實現(xiàn)分布式系統(tǒng)高可用限流器實戰(zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • Go語言題解LeetCode724尋找數(shù)組的中心下標

    Go語言題解LeetCode724尋找數(shù)組的中心下標

    這篇文章主要為大家介紹了Go語言題解LeetCode724尋找數(shù)組的中心下標,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12

最新評論