这里以封装http.Status
为例。
Error() string
接口实现Error() string
接口就可以定义一个自定义 error。
type ErrWithStatusCode struct {
msg string
code int
}
func (e ErrWithStatusCode) Error() string {
return e.msg
}
Is(error) bool
, As(interface{}) bool
接口在go 1.13后,系统库中添加了两个函数errors.Is
, errors.As
。用来处理error多重包装问题。
这也使得实现自定义error
,如果不实现Is/As两个接口,将会产生严重的兼容问题。这会使得对它调用errors.Is
/errors.As
失败。
func NewErrWithStatus(msg string, code int) error {
msg = fmt.Sprintf("%v; error status:%v", msg, code)
return &ErrWithStatusCode{msg, code}
}
type ErrWithStatusCode struct {
msg string
code int
}
func (e ErrWithStatusCode) Error() string {
return e.msg
}
func (e *ErrWithStatusCode) Is(tgt error) bool {
switch tgt.(type) {
case *ErrWithStatusCode, ErrWithStatusCode:
return true
}
return false
}
// As e、target对应 errors.As(e, target)参数
func (e *ErrWithStatusCode) As(target interface{}) bool {
switch v := target.(type) {
case *ErrWithStatusCode:
v.code = e.code
v.msg = e.msg
return true
}
return false
}
验证:
func TestErrWithStatusCode_IsAs(t *testing.T) {
err := NewErrWithStatus("hello", http.StatusBadGateway)
err1 := fmt.Errorf("%w, world", err) // 使用 fmt.Errorf(%w),添加一个包装后的error。
flag := false
if errors.Is(err1, ErrWithStatusCode{}) {// 因为实现了Is接口,可以对它使用系统库对它进行断言
flag = true
logrus.Infof("err is ErrWithStatusCode:%v", err)
}
assert.Equal(t, flag, true)
tp := new(ErrWithStatusCode)
if errors.As(err1, tp) { // 因为实现了As接口,可以还原它的具体数据
logrus.Infof("%v %v", tp.code, tp)
assert.Equal(t, tp.code, 502)
}
}