[Go-Ginrest] 1 行のコードで RESTful インターフェイスを実装

バックグラウンド

現在のマイクロサービスまたはサービス指向のアイデアに基づいて、ビジネス ロジック処理関数のほとんどは次のようになります。

たとえば、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 インターフェースとしてラップする必要がありますが、通常、次の手順を実行する必要があります。

  1. HTTPメソッド、URLを指定
  2. 認証
  3. パラメータバインディング
  4. リクエストを処理する
  5. 応答を処理する

パラメータ バインディングと処理応答はほぼ同じテンプレート コードであり、認証は基本的にテンプレート コードであることがわかります (もちろん、一部の認証はより複雑になる場合があります)。

Ginrest ライブラリは、これらのテンプレート コードを排除することです. これは複雑なフレームワークではなく、これらの繰り返しの処理を支援する単純なライブラリです. この機能を実現するために、Go1.18 ジェネリックが使用されます.

倉庫アドレス: github.com/jiaxwu/ginr…

特性

このライブラリは、次の機能を提供します。

  • RESTful 要求応答をカプセル化する
    • RESTful リクエストを標準形式のサービスにカプセル化
      • Do()、DoOpt()メソッドに対応
    • 標準形式のサービス処理結果を標準の RESTful 応答形式にカプセル化します: Rsp{code, msg, data}
      • ProcessRsp()、Success()、Failure()、FailureCodeMsg()メソッドに対応
    • デフォルトでは、統一された数値エラー コード形式が使用されます: [0, 4XXXX, 5XXXX]
    • デフォルトでは、標準エラー形式が使用されます: Error{code, msg}
      • NewError()、ToError()、ErrCode()、ErrMsg()、ErrEqual()メソッドに対応
    • デフォルトの統合ステータス コード [200、400、500]
      • 対応するエラーコード [0、4XXXX、5XXXX]
  • パニック時の応答フォーマットを統一するリカバリミドルウェアを提供
  • 要求コンテキストを格納するための SetKey()、GetKey() メソッドを提供します (ジェネリック)
  • Req (ジェネリック) を設定するための ReqFunc() を提供します。

使用例

まず、2 つの単純なサービスを実装します。

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,处理
	e.POST("/user/info/update", Verify, ginrest.Do(func(c *gin.Context, req *UpdateUserInfoReq) {
		req.UID = GetUID(c)
	}, 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": {}
}
复制代码

如果这个库的设计不符合具体的业务,也可以按照这种思路去封装一个类似的库,只要尽可能的统一请求、响应的格式,就可以减少很多重复的模板代码。

おすすめ

転載: juejin.im/post/7132790934353215502