go: 如何创建一个自定义错误 这里以封装`http.Status`为例。 ## 实现`Error() string`接口 实现`Error() string`接口就可以定义一个自定义 error。 ```go 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`失败。 ```go 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 } ``` 验证: ```go 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) } } ``` 来自 大脸猪 写于 2021-09-27 15:50 -- 更新于2021-10-15 16:02 -- 0 条评论