英文:
Working with errors in Go
问题
我现在是你的中文翻译助手,以下是你要翻译的内容:
我已经使用Go语言一段时间了,但还没有真正掌握如何处理错误。
即使是标准库也有许多不同的处理错误的方式(有些甚至不允许检查错误,只能通过字符串匹配来处理)。
我最近读了Dave Cheney的博文《检查错误》。这似乎是朝着正确方向迈出的一步,但我仍然很难真正将其应用到实际中。
假设我创建了一个名为a
的包,用于向第三方REST API(例如Facebook Graph)发送请求。我希望这个包能够暴露与API相匹配的函数,比如GetUser
。
调用GetUser
可能有以下几种结果:
- 成功
- 请求失败,比如第三方API不可用
- 第三方API返回错误(比如找不到用户)
- 响应的反序列化或类似操作失败
在第二种情况下,对错误进行断言非常有效。然而,在区分第三种和第四种情况时,这种方法就不够了。
我目前的用例是实现自己的REST API,并使用这个第一个包。在这种情况下,我希望能够分别返回200 OK
、503 Service Unavailable
、400 Bad Request
、500 Internal Server Error
等响应。
在不必从包a
中返回HTTP响应代码或类似内容的情况下,有什么好的通用方法来解决这个问题呢?
英文:
I've worked with Go for a while now and has yet to really nail how I work with errors.
Even the standard library has a number of different ways to deal with errors (some not even making it possible to inspect the errors without having to resort to string matching).
I've recently read the blog post Inspecting Errors by Dave Cheney. This sounds like its a step in the right direction, but I'm still having a hard time really putting it to real use.
Say I have made a package a
that does requests to a 3rd party REST API (such as Facebook Graph for instance). I'd like this package to expose functions matching those of the API - lets say GetUser
.
Calling GetUser
can have a number of outcomes:
- Success
- Failed due to request failure like 3rd party API being down
- 3rd party API returned an error (like user not found)
- Deserialization of response or similar failed
Asserting the error for behaviour works very well in the second case. However it falls short in distinguishing between the third and fourth case.
My current use case is implementing my own REST API that use this first package. In this case I'd like be able to return 200 OK
, 503 Service Unavailable
, 400 Bad Request
, 500 Internal Server Error
responses respectively to the possible outcomes.
What would be a good general approach to solving this problem without having to include returning http response codes or similar from package a
?
答案1
得分: 4
我会定义一些“包装错误”来表示实际发生的错误。例如:
type SerializationError struct { Error error }
func (err SerializationError) Error() string { return err.Error.Error() }
type HTTPError struct { Error error }
// ...
然后在你的 API 客户端代码中:
b, err := json.Marshal(v)
if err != nil {
return nil, SerializationError{err}
}
// ...
resp, err := client.Post(url, ct, body)
if err != nil {
return nil, HTTPError{err}
}
然后,你可以这样处理:
err := client.GetUser(id)
switch err.(type) {
case SerializationError:
// 返回 400
case HTTPError:
// 返回 500
// 其他情况
}
英文:
What I'd do is define a few "wrapper errors" to signify where error has actually happened. E.g.
type SerializationError struct { Error error }
func (err SerializationError) Error() string { return err.Error.Error() }
type HTTPError struct { Error error }
// ...
And then in the code of your API Client:
b, err := json.Marshal(v)
if err != nil {
return nil, SerializationError{err}
}
// ...
resp, err := client.Post(url, ct, body)
if err != nil {
return nil, HTTPError{err}
}
Then, you can do this:
err := client.GetUser(id)
switch err.(type) {
case SerializationError:
// respond with 400
case HTTPError:
// respond with 500
// etc.
}
答案2
得分: 0
在处理这个问题一段时间后,我发现没有一个完美的解决方案,我开始接受这个事实。
我猜这也是Dave Cheney所说的:
然而,我得出结论,没有一种单一的方法来处理错误。
由于我仍然想避免对错误进行类型断言,因为这会带来依赖性的挑战,我最终引入了一个类似于链接文章中提到的Temporary() bool
模式的Internal() bool
行为。
这样至少可以让我断言行为,而不必强制导入最初创建错误的包。
虽然不是最佳解决方案,但现在只能这样了。
Ainar-G的答案也值得一看。
英文:
After working with this for a while without coming any closer to a solution that feels just right I'm coming to terms with there not being a good single solution for this.
I guess this might also be what Dave Cheney means by:
> However, I have concluded that there is no single way to handle errors.
Since I still want to avoid type asserting the error due to the dependency challenges that brings I've ended up introducing an Internal() bool
behaviour similar to the Temporary() bool
pattern mentioned in the linked articles.
This at least allow me to assert the behavior without being forced to import the package that originally created the error.
It's not the best fit, but it will have to do for now.
Ainar-G's answer is worth taking a look at as well.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论