广告
返回顶部
首页 > 资讯 > 后端开发 > GO >Go Ginrest实现一个RESTful接口
  • 199
分享到

Go Ginrest实现一个RESTful接口

2024-04-02 19:04:59 199人浏览 薄情痞子
摘要

目录背景特性使用例子实现原理功能列表处理请求处理响应处理错误请求上下文操作请求结构体处理注背景 基于现在微服务或者服务化的思想,我们大部分的业务逻辑处理函数都是长这样的: 比如grp

背景

基于现在微服务或者服务化的思想,我们大部分的业务逻辑处理函数都是长这样的:

比如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的泛型。

仓库地址:GitHub.com/jiaxwu/ginr…

特性

这个库提供以下特性:

  • 封装RESTful请求响应
    • 封装RESTful请求为标准格式服务
    • 封装标准格式服务处理结果为标准RESTful响应格式:Rsp{code, msg, data}
    • 默认使用统一数字错误码格式:[0, 4XXXX, 5XXXX]
    • 默认使用标准错误格式:Error{code, msg}
    • 默认统一状态码[200, 400, 500]
  • 提供Recovery中间件,统一panic时的响应格式
  • 提供SeTKEy()、GetKey()方法,用于存储请求上下文(泛型)
  • 提供ReqFunc(),用于设置Req(泛型)

使用例子

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

首先我们实现两个简单的服务:

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

运行上面代码,然后尝试访问接口,可以看到返回结果:

请求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": {}
}

实现原理

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

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

以上就是Go Ginrest实现一个RESTful接口的详细内容,更多关于Go Ginrest实现RESTful接口的资料请关注编程网其它相关文章!

您可能感兴趣的文档:

--结束END--

本文标题: Go Ginrest实现一个RESTful接口

本文链接: https://www.lsjlt.com/news/121014.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Go Ginrest实现一个RESTful接口
    目录背景特性使用例子实现原理功能列表处理请求处理响应处理错误请求上下文操作请求结构体处理注背景 基于现在微服务或者服务化的思想,我们大部分的业务逻辑处理函数都是长这样的: 比如grp...
    99+
    2022-11-11
  • java实现一个接口调取另一个接口(接口一调取接口二)
    目录java一个接口调取另一个接口工具类springboot中使用(接口一)接口二接口的调用与调用别人的接口别人调用我们的接口,与controller方法开发类似我们调用别人的接口j...
    99+
    2022-11-12
  • 怎么用spring注解开发一个RESTful接口
    本篇内容主要讲解“怎么用spring注解开发一个RESTful接口”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么用spring注解开发一个RESTful接口”吧!一、开发REST接口在本专栏...
    99+
    2023-06-29
  • spring常用注解开发一个RESTful接口示例
    目录一、开发REST接口第一步:定义资源(对象)第二步:HTTP方法与Controller(动作)二、统一规范接口响应的数据格式一、开发REST接口 在本专栏之前的章节中已经给大家介...
    99+
    2022-11-13
  • SpringMVC Restful api接口实现的代码
    【前言】面向资源的 Restful 风格的 api 接口本着简洁,资源,便于扩展,便于理解等等各项优势,在如今的系统服务中越来越受欢迎。.net平台有WebAPi项目是专门用来实现Restful api的,其良好的系统封装,简洁优雅的代码实...
    99+
    2023-05-31
    springmvc restful api
  • java一个接口多个实现得调用
    在 Java 中,如果一个接口有多个实现类,可以通过以下几种方式来调用不同的实现类: 根据具体实现类的类型进行调用: InterfaceA objA = new ImplementationA();InterfaceA objB = new...
    99+
    2023-08-16
    java 代理模式 开发语言
  • C++中怎么实现一个接口
    C++中怎么实现一个接口,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。定理1:C++接口是依赖的终点。接口不需要依赖任何东西。推论1:依赖C++接口是安全的。不会带来更多的...
    99+
    2023-06-17
  • controller接口跳转到另一个controller接口的实现
    controller接口跳转到另一个controller接口 @RestController @RequestMapping("/aaa") public class TestC...
    99+
    2022-11-12
  • gorm+gin实现restful分页接口的实践
    目录1. 定义分页struct 2. 数据表Model 3. 定义分页查询搜索的结构体 4. 分页和搜索数据查询5.例子代码 API处理分页看似简单,实际上暗藏危机.最常见的分页方式...
    99+
    2022-11-12
  • Python:多态(一个接口多种实现)
    class Animal(object):----def init(self,name):--------self.name=name def talk(self): pass @staticmethod def animal_ta...
    99+
    2023-01-31
    接口 多种 多态
  • C#中怎么实现一个IDisposable接口
    这期内容当中小编将会给大家带来有关C#中怎么实现一个IDisposable接口,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。C#语言有很多值得学习的地方,这里我们主要介绍C#实现IDisposable接口...
    99+
    2023-06-17
  • C#中怎么实现一个Strategy接口
    C#中怎么实现一个Strategy接口,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。C#实现Strategy接口应用常因用户输入、运行平台和部署环境等的不同执行的任务亦不相同。...
    99+
    2023-06-17
  • Java 中怎么实现一个Runnable接口
    这期内容当中小编将会给大家带来有关Java 中怎么实现一个Runnable接口,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。但是Java Runnable 接口并没有任何对线程的支持,我们还必须...
    99+
    2023-06-17
  • WCF中怎么实现一个ICommunicationObject接口
    本篇文章为大家展示了WCF中怎么实现一个ICommunicationObject接口,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。WCF托管特性ICommunicationObject接口publi...
    99+
    2023-06-17
  • VB.NET中怎么实现一个IEnumerator接口
    这篇文章给大家介绍VB.NET中怎么实现一个IEnumerator接口,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。要VB.NET实现IEnumerator接口,需在实现以下几个函数来支持IEnumerator接口的操...
    99+
    2023-06-17
  • Python+flask实现restful接口的示例详解
    目录>1.第一个实例:HelloWorld 2.Post 方法 3.Get 方法 4.通过变量设置动态url 1.第一个实例:HelloWorld 1.编写python代...
    99+
    2023-02-08
    Python flask实现restful接口 Python restful接口 Python flask restful接口
  • java中如何写一个接口并实现
    接口的定义方式如下:[可见度] interface 接口名称 [extends 其他的接口名] { // 声明变量 // 抽象方法}接口的主体包含着抽象方法,但所有方法在接口内(定义上)都是抽象(Abstra...
    99+
    2016-02-11
    java 接口 实现
  • 如何在Spring中实现一个BeanFactoryPostProcessor接口
    本篇文章为大家展示了如何在Spring中实现一个BeanFactoryPostProcessor接口,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。接口简介BeanFactoryPostProcess...
    99+
    2023-06-06
  • Python中怎么实现一个支付接口
    这篇文章给大家介绍Python中怎么实现一个支付接口,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。首先定义数据如下:用的测试平台的数据data=[   "", ...
    99+
    2023-06-17
  • 怎么在Springmvc中实现一个Controller接口
    怎么在Springmvc中实现一个Controller接口?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Spring MVC Controller控制器,是MVC中的部分C...
    99+
    2023-05-30
    springmvc controller
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作