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 基于卷积神经网络的声纹识别
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值
  • ¥15 我想咨询一下路面纹理三维点云数据处理的一些问题,上传的坐标文件里是怎么对无序点进行编号的,以及xy坐标在处理的时候是进行整体模型分片处理的吗
  • ¥15 CSAPPattacklab
  • ¥15 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图
  • ¥15 stm32开发clion时遇到的编译问题