go-zero之错误处理

1,847 阅读3分钟

介绍

go-zero官网文档提供了一种错误处理的Demo,暂且称之为动态的错误处理机制。我们先看一下官方给的例子

动态处理

先看代码

package errorx

const defaultCode = 1001

type CodeError struct {
   Code int    `json:"code"`
   Msg  string `json:"msg"`
}

func NewCodeError(code int, msg string) error {
   return &CodeError{Code: code, Msg: msg}
}

func NewDefaultError(msg string) error {
   return NewCodeError(defaultCode, msg)
}

func (e *CodeError) Error() string {
   return e.Msg
}

优劣分析

如开篇所说的,我称之为动态处理。那么何为动态处理呢?就是同一个错误码(code),支持多个错误提示信息(msg)。如一个用户不存在的code,我们返回给前端或者客户端的错误信息会根据用户名的不同,展示不同的错误信息,如fmt.Sprintf("用户%s不存在",userName)。官方提供的这种方式明显是满足动态处理的。

在我们日常开发过程中,绝大多数的错误信息,我们是可以确定的,即一个错误码(code),只对应一个错误信息(msg)。作者之前开发的项目中,通常都是用一个map来定义静态错误集合。这里我们借鉴一下用stringer来处理错误。

静态处理

先看代码

package errorx

//go:generate stringer -linecomment -type errCode

type errCode int

// 通用错误码
const (
   ERR_OK errCode = 200 // 正确
)

// 用户服务涉及的错误码,前3100为表示用户服务
const (
   ERR_USER_NOT_EXISTS = 100001 // 用户不存在
)

stringer自动生成code与msg的映射

// Code generated by "stringer -linecomment -type errCode"; DO NOT EDIT.

package errorx

import "strconv"

func _() {
   // An "invalid array index" compiler error signifies that the constant values have changed.
   // Re-run the stringer command to generate them again.
   var x [1]struct{}
   _ = x[ERR_OK-200]
   _ = x[ERR_USER_NOT_EXISTS-100001]
}

const (
   _errCode_name_0 = "正确"
   _errCode_name_1 = "用户不存在"
)

func (i errCode) String() string {
   switch {
   case i == 200:
      return _errCode_name_0
   case i == 100001:
      return _errCode_name_1
   default:
      return "errCode(" + strconv.FormatInt(int64(i), 10) + ")"
   }
}

优劣分析

stringer根据type--errcode和注释信息,自动为errCode实现Stringer接口。 好处是,我们只需要定义错误码和错误信息。不足之处,比如动态处理我们可以定义一个map,快速检索到错误代码是否是我们自定义的错误类型?如果我们也增加一个map类型的数据来装载所有自定义的错误信息,这样就违背了我们的初衷--只关注定义错误信息。

动静结合处理

先看代码

package errorx

import "strings"

const defaultCode errCode = 1001

type CodeError struct {
   // 重点1: 将int改成errCode类型
   Code errCode `json:"code"`
   Msg  string  `json:"msg"`
}

// 根据错误码,生成自定义的错误类型
func NewCodeError(code errCode) error {
   return &CodeError{Code: code, Msg: code.String()}
}

// 动态处理错误
func NewDefaultError(msg string) error {
   return &CodeError{Code: defaultCode, Msg: msg}
}

func (e *CodeError) Error() string {
   return e.Msg
}

// 重点2: 不根据map来校验是否是自定义类型
// 判断错误码是否是我们自定义的错误类型
// 这里我们根据stringer,会为那些没有定义的code生成默认的msg来判断是否是我们自定义的code
func IsCodeError(code int) bool {
   return !strings.HasPrefix(errCode(code).String(), "errCode(")
}

优劣分析

如果是静态的错误类型,我们只需要定义好错误码,通过stringer来自动实现Stringer接口。

如果是动态的错误类型,我们只需调用NewDefaultError,传递不用的msg,复用defaultCode。

参考资料

go-zero官方动态错误处理Demo

stringer错误处理Demo

项目代码