网站推广软件免费版大全运城市网站建设
网站推广软件免费版大全,运城市网站建设,重庆企业建站公司,wordpress获取作者信息不知道大家在使用 Gin 构建 API 服务时有没有这样的问题:参数绑定的环节可不可以自动处理#xff1f;错误可不可以直接返回#xff0c;不想写空 return, 漏写就是 bug本文通过简单地封装#xff0c;利用 go 的接口特性#xff0c;提供一个解决上述两个问题的思路一、解决过…不知道大家在使用 Gin 构建 API 服务时有没有这样的问题:参数绑定的环节可不可以自动处理错误可不可以直接返回不想写空 return, 漏写就是 bug本文通过简单地封装利用 go 的接口特性提供一个解决上述两个问题的思路一、解决过程1.1 刚开始时写 API 服务时我们刚开始使用 Gin 写 API 服务时一般会按照官方文档上的 这么写// User 用户结构
type User struct {UserName string
}// CreateUser 创建用户
func CreateUser(ctx *gin.Context) {var params Userif err : ctx.ShouldBind(params); err ! nil {ctx.JSON(http.StatusBadRequest, gin.H{code: 400,msg: 参数错误,})logrus.Errorf(params err, %v, params)return}// 一些其他的业务逻辑 ...ctx.JSON(http.StatusOK, gin.H{code: 0,msg: 创建成功,})
}func main() {r : gin.Default()r.POST(user, CreateUser)if err : r.Run(:8080); err ! nil {logrus.Fatalf(can not start serve: %v, err)}
}
1.2 封装返回值我们写了一段时间之后会发现我们的返回值的结构是固定的为什么不抽象一下呢所以我们创建了一个结构体 Resp 并且封装了两个方法用于成功和失败这两种状态的返回// resp.go// Resp 返回
type Resp struct {Code intMsg stringData interface{}
}// ErrorResp 错误返回值
func ErrorResp(ctx *gin.Context, code int, msg string, data ...interface{}) {resp(ctx, code, msg, data...)
}// SuccessResp 正确返回值
func SuccessResp(ctx *gin.Context, msg string, data ...interface{}) {resp(ctx, 0, msg, data...)
}// resp 返回
func resp(ctx *gin.Context, code int, msg string, data ...interface{}) {resp : Resp{Code: code,Msg: msg,Data: data,}if len(data) 1 {resp.Data data[0]}ctx.JSON(http.StatusOK, resp)
}
添加这个方法之后我们再看一下 CreateUser 这个方法成功的从 16 行变到了 12 行// main.go
// CreateUser 创建用户
func CreateUser(ctx *gin.Context) {var params Userif err : ctx.ShouldBind(params); err ! nil {ErrorResp(ctx, 400, 参数错误)logrus.Errorf(params err, %v, params)return}// 一些其他的业务逻辑 ...SuccessResp(ctx, 创建成功)
}
1.3 两个痛点上面的方法还不够完整我们还是有许多重复的逻辑可以发现我们在写的绝大多数 API 大概都是这样参数绑定 校验业务逻辑返回这里面有两个痛点参数绑定的环节可不可以自动处理错误可不可以直接返回不想写空 return, 漏写就是 bug // 不想写大量这种重复的代码var params Userif err : ctx.ShouldBind(params); err ! nil {// 下面这三行是不是可以合并成一行ErrorResp(ctx, 400, 参数错误)logrus.Errorf(params err, %v, params)return}
1.4 使用接口封装请求上面的这两个痛点我们可以通过一个辅助函数解决// Requester 请求
type Requester interface {Request(ctx *gin.Context) (*Resp, error)
}// Handle 请求
func Handle(r Requester) gin.HandlerFunc {return func(ctx *gin.Context) {resp, err : request(r, ctx)if err ! nil {var code *errcode.Errorif !errors.As(err, code) {code errcode.Unknown.Wrap(err)}resp Resp{Code: code.Code,Msg: code.String(),}_ ctx.Error(err)}ctx.JSON(http.StatusOK, resp)}
}func request(r Requester, ctx *gin.Context) (*controller.Resp, error) {// 参数绑定if err : ctx.ShouldBind(r); err ! nil {return nil, errcode.ErrParams.Wrap(err)}return r.Request(ctx)
}
这样我们只需要实现这个 Requester, 写 API 时只需要关注业务逻辑就可以了// CreateUser 创建用户
type CreateUser struct {UserName string
}func (u *User) Request(ctx *gin.Context) (*Resp, error) {// 业务逻辑// 返回成功值
}func main() {r : gin.Default()r.POST(user, Handle(CreateUser))if err : r.Run(:8080); err ! nil {logrus.Fatalf(can not start serve: %v, err)}
}
上面的代码有一个 bug 不知道大家发现没有我们上一次请求的参数会被带到下一次请求当中// Handle 请求
func Handle(r Requester) gin.HandlerFunc {return func(ctx *gin.Context) {// 创建一个新的 Requester, 避免将上一次的参数带到下一次当中if reflect.TypeOf(r).Kind() ! reflect.Ptr {panic(must be a pointer)}req : reflect.New(reflect.ValueOf(r).Elem().Type()).Interface().(Requester)resp, err : request(req, ctx)if err ! nil {var code *errcode.Errorif !errors.As(err, code) {code errcode.Unknown.Wrap(err)}resp Resp{Code: code.Code,Msg: code.String(),}_ ctx.Error(err)}ctx.JSON(http.StatusOK, resp)}
}
二、总结大概这样差不多就 ok 了还有很多可以完善的点这里有一些思路有的已经做了有的还在路上每次注册都写 Handle(CreateUser) 还是有点麻烦?可以封装一下 gin.IRouter 这个接口这样注册接口就可以和原来一样了2. 参数绑定如果我需要多次绑定怎么办?可以添加一个接口如果实现了这个接口就执行以下对于有特殊的参数校验之类的也可以采用类似的方式处理 type Binder interface {Bind(ctx *gin.Context) error}func request(r Requester, ctx *gin.Context) (*controller.Resp, error) {// 参数绑定if err : ctx.ShouldBind(r); err ! nil {return nil, errcode.ErrParams.Wrap(err)}// 其余参数绑定if b, ok : r.(Binder); ok {if err : b.Bind(api); err ! nil {return nil, errcode.ErrParams.Wrap(err)}}return r.Request(ctx)}
3. 怎么输出 API 文档可以和 swagger 之类的 API 文档结合, 利用 go generate 自动生成顺便可以连接口注册都不用了添加一行注释自动注册接口并且输出接口文档 // Router put /api/v1/userfunc(u *User) Request(ctx *gin.Context) (*Resp, error)
4. 能不能减少 CURD 代码?可以实现只需要采用约定的项目接口可以 利用 go generate 直接自动生成简单的 CURD 代码博客原文Go Web 小技巧一简化Gin接口代码lailin.xyz
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/91824.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!