在Go中处理错误

4
我已经使用Go一段时间了,但还没有真正掌握如何处理错误。
即使是标准库也有许多不同的处理错误的方式(有些甚至不允许检查错误而必须采用字符串匹配)。
我最近读了Dave Cheney的博客文章Inspecting Errors。这似乎是朝着正确方向迈出的一步,但我仍然很难真正将其应用到实际中。
假设我已经创建了一个名为a的包,该包对第三方REST API进行请求(例如Facebook Graph)。我希望这个包暴露与API相匹配的函数-比如说GetUser
调用GetUser可能会有以下几种结果:
  1. 成功
  2. 由于第三方API失败而失败,例如API宕机
  3. 第三方API返回错误(例如找不到用户)
  4. 响应的反序列化或类似操作失败
在第二种情况下,断言行为的错误非常有效。然而,在区分第三种和第四种情况方面,它存在缺陷。
我的当前用例是实现自己的REST API,使用这个第一个包。在这种情况下,我希望能够分别返回 200 OK503 Service Unavailable400 Bad Request500 Internal Server Error 响应,以响应可能的结果。如何在不必从包 a 返回HTTP响应代码或类似内容的情况下解决这个问题?

1
Dave Cheney 写的另一篇关于错误处理的有趣文章 - John S Perayil
是的,只需阅读John S Perayil建议的文章。 - Yandry Pozo
他还做了一个关于此的不错演讲 - Kasper Middelboe Petersen
我在发布这个问题之前已经阅读/观看了它们。问题的提出是因为我不知道如何将它们应用到所描述的问题上。 - Kasper Middelboe Petersen
2个回答

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:
    // respond with 400
case HTTPError:
    // respond with 500
// etc.
}

Dave Cheney博客的要点是应该避免类型断言错误。当所有可能的错误都需要其自己的类型时,它也会迅速升级,以便调用者可以判断如何处理链条上进一步的错误。 - Kasper Middelboe Petersen

0

在不断尝试却无法找到最佳解决方案的情况下,我开始接受这个问题没有好的单一解决方案。

我想这也许是Dave Cheney所说的:

然而,我得出结论:处理错误没有单一方式。

由于我仍然希望避免类型断言错误,因为它会带来依赖性挑战,所以我引入了一个类似于链接文章中提到的Temporary() bool模式的Internal() bool行为。

至少这样可以让我断言行为,而不必强制导入原始创建错误的包。

虽然不是最佳选择,但目前只能这样做了。

值得一看的还有Ainar-G的答案


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接