色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

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

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

3天內不再提示

包裝RESTful形式的接口步驟

馬哥Linux運維 ? 來源:馬哥Linux運維 ? 作者:馬哥Linux運維 ? 2022-09-22 09:52 ? 次閱讀

背景

基于現在微服務或者服務化的思想,我們大部分的業務邏輯處理函數都是長這樣的:

比如grpc服務端:

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

grpc客戶端:

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

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

指定HTTP方法、URL

鑒權

參數綁定

處理請求

處理響應

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

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

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

特性

這個庫提供以下特性:

封裝RESTful請求響應

封裝RESTful請求為標準格式服務

封裝標準格式服務處理結果為標準RESTful響應格式:Rsp{code, msg, data}

默認使用統一數字錯誤碼格式:[0, 4XXXX, 5XXXX]

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

默認統一狀態碼[200, 400, 500]

提供Recovery中間件,統一panic時的響應格式

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

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

使用例子

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

首先我們實現兩個簡單的服務:

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) } // 這里拆多一步是為了顯示第一個參數是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)}

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

請求1GET http://127.0.0.1:8000/user/info/get{    "uid": 10}響應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}響應2{    "code": 40100,    "msg": "user not exists"}
請求3POST http://127.0.0.1:8000/user/info/update{    "username": "jiaxwu",    "age": 10}響應3{    "code": 0,    "msg": "ok",    "data": {}}

實現原理

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

// 處理請求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) {  // 參數綁定  req, err := BindJSON[Req](c)  if err != nil {   return  }  // 進一步處理請求結構體  if reqFunc != nil {   reqFunc(c, req)  }  var rsp *Rsp  // 業務邏輯函數調用  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()函數。

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

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

// 參數綁定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等數據封裝為一個JSON格式響應體,對應ProcessRsp()、Success()、Failure()、FailureCodeMsg()函數。

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

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

響應格式統一為:

// 響應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()則可以使用這些方法。

處理錯誤

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

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

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

比如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存起來,這樣可以在業務邏輯里使用。

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

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

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

請求結構體處理

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

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

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

    關注

    33

    文章

    8575

    瀏覽量

    151019
  • 函數
    +關注

    關注

    3

    文章

    4327

    瀏覽量

    62573
  • 代碼
    +關注

    關注

    30

    文章

    4779

    瀏覽量

    68524

原文標題:一行代碼實現一個 RESTful 接口

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

收藏 人收藏

    評論

    相關推薦

    請問LabVIEW通過無線WiFi采集數據 基于restful

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

    編寫restful

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

    ISTA 國際包裝運輸組織---電子產品包裝運輸考察

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

    restful api設計規范

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

    python restful api學習技巧精選2

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

    一文知道后端接口開發json,jsonp,restful

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

    什么是restful以及restfulAPI的設計風格?

    如何理解restful架構?什么是restful API ? restful API的設計風格和序列化?restful API之請求與響應 ?
    發表于 11-04 08:25

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

    什么是接口?為什么需要接口接口硬件包含哪些部分?接口軟件有什么功能?接口電路有哪些形式?什么是
    發表于 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
    發表于 02-17 07:31

    車載逆變電源包裝

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

    Constrained RESTful Environments (CoRE) Link Format

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

    Restful 和 RPC 是什么關系與區別

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

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

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

    構建RESTful Web服務的過程

    本指南將引導您完成使用 Spring 創建“Hello, World”RESTful Web 服務的過程。
    的頭像 發表于 09-06 15:47 ?695次閱讀

    使用RESTful Web服務的過程

    本指南將引導您完成創建使用#spring# #spring認證# RESTful Web 服務的應用程序的過程。
    的頭像 發表于 09-06 15:47 ?704次閱讀
    主站蜘蛛池模板: A国产一区二区免费入口| 老板揉搓秘书丰满大乳| 少男同志freedeos| free性中国hd护士高清| 乱码午夜-极品国产内射| 亚洲中文字幕在线第六区| 国产麻豆精品人妻无码A片| 人妻少妇偷人精品无码洋洋AV| 91国内精品久久久久免费影院| 久久精品电影久久电影大全| 亚洲国产精品热久久| 国产精品婷婷久青青原| 三级黄色小视频| 俄罗斯摘花| 涩涩伊人久久无码欧美| 调教玩弄奶头乳夹开乳震动器| 欧美最猛黑人XXXXWWW| 99在线观看免费| 欧美日韩免费看| AV多人爱爱XXx| 欧美黑人巨大videos免费| av av在线| 欧式午夜理伦三级在线观看| japanese from色系| 强奷表妺好紧2| 波多野结衣的AV一区二区三区| 全黄h全肉细节文在线观看| brazzers欧美孕交| 欧美亚洲精品真实在线| qvod 在线| 日韩欧美精品有码在线播放| 纯肉高H啪短文合集| 少妇第一次交换| 国产蜜臀AV在线一区视频| 性满足久久久久久久久| 国产在线观看香蕉视频| 亚洲乱码国产乱码精品精98| 久久99re2在线视频精品| 中文国产乱码在线人妻一区二区| 两个女人互添下身高潮自视频| 91精品一区二区三区在线观看|