【问题标题】:Using panics in web application在 Web 应用程序中使用恐慌
【发布时间】:2014-11-04 13:56:36
【问题描述】:

我正在用 Go 编写我的 Web 应用程序。我想将大部分错误从 API 转换为恐慌,然后在更高级别的函数中捕获这些恐慌,记录它们并将错误页面返回给用户。

类似这样的:

func Handler(body func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
    return func(responseWriter http.ResponseWriter, request *http.Request) {
        defer recoverIfPanic(responseWriter, request)
        body(responseWriter, request)
    }
}

func recoverIfPanic(responseWriter http.ResponseWriter, request *http.Request) {
    reason := recover()
    if reason == nil {
        return
    }
    // log and return http error
}

func PanicIf(err error, httpStatus int, description string) {
    if error != nil {
        panic(MyPanicStruct{err: err, httpStatus: httpStatus, description: description})
    }
}

在我的实际代码中

result, err := SomeApi(...)
PanicIf(err, http.StatusInternalServerError, "SomeApi")

在 99% 的情况下,我无法做任何合理的事情,例如SQL 服务器返回意外错误或文件系统丢失文件,我只想记录这种情况并将错误返回给用户。所以我看不出我应该手动返回“err”展开堆栈的任何原因,实际上我会丢失堆栈跟踪和上下文,并且更难找到错误原因。

有什么我想念的,所以这种方法不能很好地工作吗?似乎大多数 Go 文章都建议不要使用恐慌/恢复,但我不明白为什么。它看起来与 Java(和类似语言)中良好的老式 throw-catch 机制一模一样,并且非常适用于 Web 应用程序。

【问题讨论】:

    标签: web-applications error-handling go


    【解决方案1】:

    有什么我想念的,所以这种方法不能很好地工作吗?

    今天(!)2014 年 11 月 4 日,Dave Cheney 在“Error handling vs. exceptions redux”中讨论了这一点

    C++ 异常仍然像三年前一样难以安全使用。当调用堆栈的任何部分都可能在没有警告的情况下爆炸时,难怪有这么多 C++ 商店要求不使用异常。

    它指的是“Why Go gets exceptions right”(2012 年,Go1.0 之前,但今天仍然有效):

    Go 确实有一个名为 panic 的工具,如果你眯着眼睛足够用力,你可能会认为 panic 和 throw 是一样的,但你错了。
    当你抛出异常时,你就会把它变成调用者的问题

    throw new SomeoneElsesProblem();
    

    例如,在 C++ 中,当您无法将 enum 转换为等效的 string 时,或者在 Java 中从字符串中解析日期时,您可能会引发异常。
    在互联网连接的世界中,来自网络的每个输入都必须被视为敌对,将字符串解析为日期的失败真的很例外吗?当然不是。

    当你在围棋中惊慌失措时,你吓坏了,这不是别人的问题,而是人类的游戏。

    panic("inconceivable")
    

    panics 对您的程序总是致命的。
    在恐慌时,您永远不要认为您的调用者可以解决问题。因此,panic 仅在特殊情况下使用,即您的代码无法继续运行的情况,或任何集成您的代码的人。

    在 Go 中不包含异常的决定是其简单性和正交性的一个例子。使用多个返回值和一个简单的约定,Go 解决了让程序员知道什么时候出错并为真正的异常保留恐慌的问题。


    使用err 的其他方法在官方wiki 页面“Error handling and Go”中进行了讨论。

    话虽如此,文章“Defer, Panic, and Recover”确实提到了一个真实的恐慌案例(json package(d *decodeState) unmarshal method),并添加:

    Go 库中的约定是,即使 当一个包在内部使用 panic 时,它的外部 API 仍然会显示明确的错误返回值

    因此,如果您对 panic 的使用严格来说是一种内部使用,那可能会奏效。

    【讨论】:

      【解决方案2】:

      所以我个人的观点是恐慌是魔鬼的工作,几乎不应该使用。

      话虽如此,我将我所有的 http 处理程序都包装在恐慌恢复中,因为有很多事情(包括 stdlib)会导致恐慌,你必须处理它 - 关闭你的网络服务器不是正确的响应 - 同意。

      我们所做的是用我们自己的错误包覆盖我们的错误包,因此当我们生成错误时,错误本身以及回溯会被发送到一个站点,以便您可以在那里记录/跟踪它。

      例如原生 http 实现 -> https://deferpanic.com/help/exception-handling/native

      我相信这种方法可以让你在不惊慌/恢复的情况下吃蛋糕。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-07-04
        • 1970-01-01
        • 2019-10-07
        • 2014-09-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多