一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

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

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

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

包裝RESTful形式的接口步驟

馬哥Linux運(yùn)維 ? 來(lái)源:馬哥Linux運(yùn)維 ? 作者:馬哥Linux運(yùn)維 ? 2022-09-22 09:52 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

背景

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

比如grpc服務(wù)端:

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

grpc客戶(hù)端:

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ù)綁定

處理請(qǐng)求

處理響應(yīng)

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

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

倉(cāng)庫(kù)地址:https://github.com/jiaxwu/ginrest

特性

這個(gè)庫(kù)提供以下特性:

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

封裝RESTful請(qǐng)求為標(biāo)準(zhǔn)格式服務(wù)

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

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

默認(rèn)使用標(biāo)準(zhǔn)錯(cuò)誤格式:Error{code, msg}

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

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

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

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

使用例子

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

首先我們實(shí)現(xiàn)兩個(gè)簡(jiǎn)單的服務(wù):

const ( ErrCodeUserNotExists = 40100 // 用戶(hù)不存在)
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()里面每個(gè)接口都只需要一行代碼!

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) }}
// 注冊(cè)請(qǐng)求func Register(e *gin.Engine) { // 簡(jiǎn)單請(qǐng)求,不需要認(rèn)證 e.GET("/user/info/get", ginrest.Do(nil, GetUserInfo)) // 認(rèn)證,綁定UID,處理        reqFunc := func(c *gin.Context, req *UpdateUserInfoReq) {  req.UID = GetUID(c) } // 這里拆多一步是為了顯示第一個(gè)參數(shù)是ReqFunc e.POST("/user/info/update", Verify, ginrest.Do(reqFunc, UpdateUserInfo))}
const ( KeyUserID = "KeyUserID")
// 簡(jiǎn)單包裝方便使用func GetUID(c *gin.Context) int { return ginrest.GetKey[int](c, KeyUserID)}
// 簡(jiǎn)單包裝方便使用func SetUID(c *gin.Context, uid int) { ginrest.SetKey(c, KeyUserID, uid)}
// 認(rèn)證func Verify(c *gin.Context) { // 認(rèn)證處理 // ...        // 忽略認(rèn)證的具體邏輯 SetUID(c, 10)}

運(yùn)行上面代碼,然后嘗試訪(fǎng)問(wèn)接口,可以看到返回結(jié)果:

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

實(shí)現(xiàn)原理

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

// 處理請(qǐng)求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  }  // 進(jìn)一步處理請(qǐng)求結(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) }}

功能列表

處理請(qǐng)求

用于把一個(gè)標(biāo)準(zhǔn)服務(wù)封裝為一個(gè)RESTfulgin.HandlerFunc,對(duì)應(yīng)Do()、DoOpt()函數(shù)。

DoOpt()相比于Do()多了一個(gè)opts參數(shù),因?yàn)楹芏鄏pc框架客戶(hù)端都有一個(gè)opts參數(shù)作為結(jié)尾。

還有一個(gè)BindJSON(),用于把請(qǐng)求體包裝為一個(gè)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}

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

處理響應(yīng)

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

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

// 處理簡(jiǎn)單響應(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()用于處理成功情況:

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

其余同理。

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

處理錯(cuò)誤

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

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

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

比如NewError()生成一個(gè)Error類(lèi)型error:

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

請(qǐng)求上下文操作

Gin的請(qǐng)求是鏈?zhǔn)教幚淼?,也就是多個(gè)handler順序的處理一個(gè)請(qǐng)求,比如:

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

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

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

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

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

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

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

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

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

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

    關(guān)注

    33

    文章

    9005

    瀏覽量

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

    關(guān)注

    3

    文章

    4381

    瀏覽量

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

    關(guān)注

    30

    文章

    4900

    瀏覽量

    70757

原文標(biāo)題:一行代碼實(shí)現(xiàn)一個(gè) RESTful 接口

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

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

掃碼添加小助手

加入工程師交流群

    評(píng)論

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

    請(qǐng)問(wèn)LabVIEW通過(guò)無(wú)線(xiàn)WiFi采集數(shù)據(jù) 基于restful

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

    編寫(xiě)restful

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

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

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

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

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

    python restful api學(xué)習(xí)技巧精選2

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

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

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

    什么是restful以及restfulAPI的設(shè)計(jì)風(fēng)格?

    如何理解restful架構(gòu)?什么是restful API ? restful API的設(shè)計(jì)風(fēng)格和序列化?restful API之請(qǐng)求與響應(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

    車(chē)載逆變電源包裝

    車(chē)載逆變電源包裝              包裝主要是指車(chē)載應(yīng)急電源在運(yùn)輸、銷(xiāo)售過(guò)程中的包裝
    發(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ū)別

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

    彈簧包裝機(jī)換包裝薄膜步驟的簡(jiǎn)單介紹

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

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

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

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

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