dtz33344 2014-11-04 13:56
浏览 75
已采纳

在Web应用程序中使用恐慌

I'm writing my web application with Go. I want to convert most of the errors from API to panics, and then catch those panics in higher-level function, log them and return error page to user.

Something like this:

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})
    }
}

and in my actual code

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

In 99% cases I can't do anything reasonable when, e.g. SQL server returns unexpected error or file is missing from filesystem, all I want is to log this situation and return error to user. So I can't see any reason I should return "err" unwinding stack manually, actually I'll lost stacktrace and context and it would be more difficult to find error reason.

Is there anything I miss so this approach won't work well? It seems that most Go articles recommend against using panic/recover, but I don't see why. It looks exactly like good old throw-catch mechanism in Java (and similar languages) and it works perfectly for web applications.

  • 写回答

2条回答 默认 最新

  • doufei7464 2014-11-04 14:04
    关注

    Is there anything I miss so this approach won't work well?

    That is discussed today (!) Nov. 4th, 2014, by Dave Cheney in "Error handling vs. exceptions redux"

    C++ exceptions, remain as difficult to use safely as they did three decades ago. When any part of your call stack can explode without warning, it is no wonder so many C++ shops mandate that exceptions not be used.

    It refers to "Why Go gets exceptions right" (2012, pre Go1.0, but still valid today):

    Go does have a facility called panic, and if you squint hard enough, you might imagine that panic is the same as throw, but you’d be wrong.
    When you throw and exception you’re making it the caller’s problem

    throw new SomeoneElsesProblem();
    

    For example in C++ you might throw an exception when you can’t convert from an enum to its string equivalent, or in Java when parsing a date from a string.
    In an internet connected world, where every input from a network must be considered hostile, is the failure to parse a string into a date really exceptional? Of course not.

    When you panic in Go, you’re freaking out, it’s not someone elses problem, it’s game over man.

    panic("inconceivable")
    

    panics are always fatal to your program.
    In panicing you never assume that your caller can solve the problem. Hence panic is only used in exceptional circumstances, ones where it is not possible for your code, or anyone integrating your code to continue.

    The decision to not include exceptions in Go is an example of its simplicity and orthogonality. Using multiple return values and a simple convention, Go solves the problem of letting programmers know when things have gone wrong and reserves panic for the truly exceptional.


    Other approaches, using err are discussed in the official wiki page "Error handling and Go".

    That being said, the article "Defer, Panic, and Recover" do mention a real case for panic (json package, (d *decodeState) unmarshal method), and add:

    The convention in the Go libraries is that even when a package uses panic internally, its external API still presents explicit error return values.

    So if your use of panic is strictly an internal one, that could work.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 无线电能传输系统MATLAB仿真问题
  • ¥50 如何用脚本实现输入法的热键设置
  • ¥20 我想使用一些网络协议或者部分协议也行,主要想实现类似于traceroute的一定步长内的路由拓扑功能
  • ¥30 深度学习,前后端连接
  • ¥15 孟德尔随机化结果不一致
  • ¥15 apm2.8飞控罗盘bad health,加速度计校准失败
  • ¥15 求解O-S方程的特征值问题给出边界层布拉休斯平行流的中性曲线
  • ¥15 谁有desed数据集呀
  • ¥20 手写数字识别运行c仿真时,程序报错错误代码sim211-100
  • ¥15 关于#hadoop#的问题