关于错误首先要说的是,只是因为有一个错误界面
type error interface {
Error() string
}
并不意味着从任何给定方法返回的error 只能附加该方法/信息。
一种常用的方法是定义自己的错误接口:
type myError interface {
error // embeds the standard error interface
OtherMethod() string // can define own methods here
}
在编写方法和函数时,非常重要返回 error 而不是 myError,否则您将该方法与错误实现结合起来,并在以后为您自己造成依赖噩梦。
现在我们已经决定可以从错误中返回额外信息,使用我们自己的错误接口,您有 3 个主要选择。
- 前哨错误
- 错误失败类型
- 行为错误
前哨错误
哨兵错误是定义为包级别变量的错误值,被导出并允许比较以检查错误。
package myPackage
var ErrConnectionFailed = errors.New("connection failed")
func Connect() error {
// trimmed ...
return ErrConnectionFailed
}
这个例子的消费者可以使用连接函数:
if err := myPackage.Connect(); err == myPackage.ErrConnectionFailed {
// handle connection failed state
}
您可以做一个比较来检查返回的错误是否等于包的哨兵错误。缺点是使用errors.New("connection failed") 创建的任何错误都是相等的,并且不仅仅是来自myPackage 的错误。
错误失败类型
略好于哨兵错误的是错误失败类型。
我们已经看到您可以定义自己的错误接口,如果我们说现在是:
type MyError interface {
error
Failure() string
}
type Err struct {
failure string
}
func (e *Err) Error() string {
// implement standard error
}
func (e *Err) Failure() string {
return e.failure
}
const ConnFailed = "connection failed"
err := &Err{failure: ConnFailed}
在消费者代码中你会得到一个错误,检查它是否实现了MyError,然后用它来处理。
err := myPackage.Connect()
if myErr, ok := err.(myPackage.MyError); ok {
// here you know err is a MyError
if myErr.Failure() == myPackage.ConnFailed {
// handle connection failed, could also use a switch instead of if
}
}
现在您知道是什么导致了错误,这很好。但是你真的在乎是什么原因吗?或者你真的只关心你想要做什么来处理那个错误。
这是行为错误很好的地方。
行为错误
这类似于定义您自己的错误类型,但您可以定义报告有关该错误的信息的方法。鉴于上面的示例,您真的关心连接失败,还是只关心是否可以重试或是否需要再次错误调用堆栈?
package myPackage
// this interface could report if the error
// is temporary and if you could retry it
type tempErr interface {
Temporary() bool
}
func (e *Err) Temporary() bool {
// return if the error is temporary or not
}
现在在消费者中(注意你不需要使用myPackage.tempErr),如果错误是暂时的,你可以使用类型断言进行测试并处理重试情况:
err := myPackage.Connect()
if tmp, ok := err.(interface { Temporary() bool }); ok && tmp.Temporary() {
// the error is temporary and you can retry the connection
}
要回答这个问题,如果没有您尝试实施的服务的具体细节,就很难说。但作为广泛的建议,我会尽量使用这 3 个示例中的最后一个。
如果您的服务使用者向您发送了一些无效的输入:
err := doThing(...)
if inv, ok := err.(interface { Invalid() bool }); ok && inv.Invalid() {
// input is invalid, return 400 bad request status code etc.
}
如果您想将特定消息返回给消费者,您可以将其设为您的错误类型的方法。警告:这将使您的包知道它们正在用于 Web 服务等。
err := doThing(...)
if msg, ok := err.(interface { ResponseMsg() string }); ok {
// write the message to the http response
io.WriteString(response, msg.ResponseMsg())
}
TL;DR您需要处理所有情况,但您可以创建错误类型,使代码更易于使用!