Go语言中常见100问题-#50 comparing an error type

介绍了可以通过%w对error进行wrap。但是一旦对error通过%w进行包装,后续在对包装后的error通过type进行判断的时候,必须采用合适的方法,否则将会出错。

下面通过一个具体的HTTP handler例子进行说明,该例功能是查询某个给定账号的交易金额。handler处理逻辑是从请求中获取账号id,然后从数据库中查询该账号的交易金额。

下面的两种情况都会导致查询失败:

  1. 请求的ID无效(id字符串的长度不是5)
  2. 查询数据库失败

情况1,我们想返回一个StatusBadRequest (400)错误。情况2,我们想返回一个ServiceUnavailable (503)错误。为了实现这个目标,我们创建一个transientError类型的错误来暂时标记这个错误。调用方检查返回的error类型,如果是transientError类型,返回503错误码,否则返回400错误码。实例代码如下:

type transientError struct {
     err error
}

func (t transientError) Error() string {
     return fmt.Sprintf("transient error: %v", t.err)
}

func getTransactionAmount(transactionID string) (float32, error) {
     if len(transactionID) != 5 {
             return 0, fmt.Errorf("id is invalid: %s", transactionID)
     }

     amount, err := getTransactionAmountFromDB(transactionID)
     if err != nil {
             return 0, transientError{err: err}
     }
     return amount, nil
}

如果是id无效,getTransactionAmount返回一个fmt.Error错误,如果是DB获取数据失败,返回经过包装的transientError错误。调用者handler方法中判断返回的error类型来决定返回合适的状态码。通过switch type,如果是DB查询失败,返回503,否则返回400。

 总结起来,如果采用Go1.13的方法wrap error, 必须采用errors.As函数检查错误类型,因为errors.As会递归的unwrap error,判断每一层的error类型是否是需要匹配的目标类型,无论返回的error是否是经过wrap的,都可以匹配检查到。

posted @ 2022-06-21 22:26  Tracydzf  阅读(69)  评论(0编辑  收藏  举报