Go语言中常见100问题-#50 comparing an error type
介绍了可以通过%w对error进行wrap。但是一旦对error通过%w进行包装,后续在对包装后的error通过type进行判断的时候,必须采用合适的方法,否则将会出错。
下面通过一个具体的HTTP handler例子进行说明,该例功能是查询某个给定账号的交易金额。handler处理逻辑是从请求中获取账号id,然后从数据库中查询该账号的交易金额。
下面的两种情况都会导致查询失败:
- 请求的ID无效(id字符串的长度不是5)
- 查询数据库失败
情况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的,都可以匹配检查到。