V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
dzdh
V2EX  ›  Go 编程语言

go 没有异常 怎么判断逻辑以外的错误 全局的处理

  •  
  •   dzdh · 1 天前 · 2407 次点击

    比如数据库服务吧。

    程序启动,先连 db 。连成功。启动 web 服务。然后 setup 路由啥的一堆。

    好,服务启动成功了。

    现在接受 http 请求,此时数据库崩了。

    gorm 返回了 err 。比如代码如下

    // userRepository.go
    func GetUser(uid int64) (*User, error) {
        user:=new(User)
        if err :=db.model(user).Find(user).err; err != nil {
            return nil,err
        } else { return user, nil }
    }
    

    按照 java/php 这种的逻辑。我可以抛出个异常。然后有个地方是处理这个特殊的异常。返回 500,db no connection 。

    go 里边咋做呢?现在数据库崩了以后,被业务中间件拦截到了 返回 401 unauthorized 。

    repository 由 http 服务调用。我要直接 panic 吗 0.0 http 的中间件 recover 住判断 err 是哪种错误? 这么粗暴的吗?

    第 1 条附言  ·  21 小时 56 分钟前
    综合起来看。成熟的 go 项目。应该是各种一路 return nil, err..
    33 条回复    2024-12-29 11:20:24 +08:00
    voidmnwzp
        1
    voidmnwzp  
       1 天前 via iPhone
    跟 java 一样向上抛啊
    sduoduo233
        2
    sduoduo233  
       1 天前 via Android
    我一般是返回 500 ,然后记录一下错误
    u,err:=GetUser(1)
    if err!=nil {
    w.WriteHeader(500)
    log.Println(err)
    return
    }
    dzdh
        3
    dzdh  
    OP
       1 天前
    @voidmnwzp @sduoduo233

    其实返回个 401 也没毛病。就是觉得怪怪的
    laikick
        4
    laikick  
       1 天前
    Panic + Recover + 自定义错误类型 就可以实现类似 Java 的操作 不过也仅仅是类似. 但是,这不是 go 推荐的.

    别把 Java 的习惯带入 go 里面. 这两个语言的思想差别很大的.

    你应该定义一个 ErrDBConnection. 然后在 http 服务 errors.Is(err, repository.ErrDBConnection).
    kk2syc
        5
    kk2syc  
       1 天前
    可以直接 panic ,可以用中间件捕获。不一定比全局优雅,但是也是不错的,业务上我都这样。
    ----

    func ExceptionMiddleware(c *gin.Context) {
    defer func() {
    if err := recover(); err != nil {
    c.JSON(500, gin.H{"msg": "ERROR"})
    c.Abort()
    }
    }()
    c.Next()
    }
    kingcanfish
        6
    kingcanfish  
       1 天前
    java 有个地方处理这个特殊异常,go 中间件不就是这个特殊的地方吗

    我一般是自己定义一些 error 比如 db error, logic errr, client err, 在最先抛出 error 的地方包一层, 然后网上传 最终中间根据不同的 error 返回错误码 ,比如你这个例子, 发现 db 包 error 了 我就包一层 自己的 db error ,往上抛, 中间件捕获
    dzdh
        7
    dzdh  
    OP
       1 天前
    @kingcanfish 抛就是 panic 呗?
    realpg
        8
    realpg  
       1 天前
    还是不建议这么跨舒适区干活
    玩惯了 exception 那一套 改 go 推荐这一套 可能一年都改不过来
    不如再找个 java 工作
    kingcanfish
        9
    kingcanfish  
       1 天前
    @dzdh #7 error
    lasuar
        10
    lasuar  
       1 天前
    改不过来习惯 ,不建议写 go ,难受自个。编程范式都不一样了。
    zoharSoul
        11
    zoharSoul  
       1 天前
    @realpg #8 人家是 php 干嘛找 java 的
    php 都是转 go 的
    afxcn
        12
    afxcn  
       1 天前
    repository 返回正确的错误类型就好了。
    Blackbelly
        13
    Blackbelly  
       1 天前   ❤️ 2
    直接 panic

    因为数据库崩了属于 unrecoverable 的错误。这时候当成 err 向上抛没有意义,上一层也无法处理,只能层层往上抛。
    而且,你的接口语义是 GetUser ,本身就不应该返回一个接口语义之外的错误。
    按照接口语义,应该是返回一个 user ,或者是 NotFoundErr ,除此之外的错误都不应该返回。
    chen11
        14
    chen11  
       23 小时 42 分钟前
    我上周才遇见个 bug ,go 程序直接崩溃,log 没打出来,找不到 bug 在哪里。习惯了 java ,来写 go 就难受
    zsj950618
        15
    zsj950618  
       23 小时 40 分钟前
    > 被业务中间件拦截到了 返回 401 unauthorized

    那是这个中间件垃圾,都不看错误类型就一股脑返回 401 。
    chevalier
        16
    chevalier  
       23 小时 19 分钟前
    从功能上来说
    Go 的 error 相当于 Java 的异常
    Go 的 panic 相当于 Java 的 Error
    changz
        17
    changz  
       22 小时 48 分钟前 via Android
    用 protobuf 定义错误码,一层一层往上抛
    wangritian
        18
    wangritian  
       22 小时 34 分钟前
    所有语言的最佳实践,都可以在流行框架内找到,推荐到 goframe 看看
    go 一般是自己设计一个符合 error 接口(包含 Error 方法)的带 code 和 msg 的自定义 error
    通过 recover 全局拦截异常,如果是底层报错(数据库连接失败等业务层无须接收),直接 panic
    如果是业务异常(用户名重复),return 自定义 error
    guanzhangzhang
        19
    guanzhangzhang  
       22 小时 25 分钟前
    返回 error ,上层处理和家 warp 信息,最后到你接口层面你可以返回 500
    leonshaw
        20
    leonshaw  
       22 小时 21 分钟前 via Android
    我一般只有断言失败才用 panic ,其它情况都是返回 error ,视情况包装一层。
    lqs
        21
    lqs  
       21 小时 49 分钟前
    ctx 要一路带进去(适当的地方加上 WithTimeout / WithCancel 等)
    err 要一路带出来(适当的地方包裹一下加上错误提示和相关参数上下文)
    iyaozhen
        22
    iyaozhen  
       20 小时 20 分钟前
    学 go 有个段子:和你们这些搞 java 的讲不清楚
    jnyanmeng
        23
    jnyanmeng  
       18 小时 39 分钟前
    @iyaozhen java 以外我都可以这样讲:和你们这些搞 java 的讲不清楚😄
    Ghostisbored
        24
    Ghostisbored  
       18 小时 23 分钟前
    每次看到一些回复都看得好笑 用个开发语言还用出优越感来了。php 流行的时候: 有新人问问题 你不适合 php 还是回去吧 ; java 流行的时候:你不适合 java 还是回去吧 ; go 近几年因为云原生 加上国内字节用的多 不少项目开始用了 又开始了:你不适合 go 还是去搞 java 吧 ; rust 现在被大家喜欢很多软件开始锈化 是不是过一段时间 要出现 你不适合搞 rust 快回去搞 go 吧🙄
    rower
        25
    rower  
       17 小时 38 分钟前
    1.有处理错误的中间件,有处理 panic 的中间件,这两个是不一样的

    2.这里是错误类型,我们走处理错误的中间件

    3.你想返回的 http 状态码是 500 ,同时错误信息是 db no ....

    却被 401 处理了

    对于这种情况是创建自定义错误类型,参考

    https://github.com/ardanlabs/service6-video/tree/main/app/api/errs

    ```
    // Error represents an error in the system.
    type Error struct {
    Code ErrCode `json:"code"`
    Message string `json:"message"`
    }
    ```

    这里的 message 就是我们的错误信息`db no ...`

    这里的 code 是我们内部错误的编码,比如说授权错误,code = 1 ,数据库错误,code = 2

    参考 code 设计

    https://github.com/ardanlabs/service6-video/blob/main/app/api/errs/codes.go

    4.如何将 不同的错误转换为对应的 http 状态码,需要建立 code 和 http 状态码的 map 关系

    参考

    https://github.com/ardanlabs/service6-video/blob/main/api/http/api/mid/errors.go

    5.错误中间件处理错误

    这里处理错误时,如果是我们自定义的错误,就将 code 转换为对应的 http 状态码,错误信息不变。

    如果不是自定义错误,表明是未知错误,返回 {500,unknow err}

    参考 https://github.com/ardanlabs/service6-video/blob/main/app/api/mid/errors.go

    ```
    func Errors(ctx context.Context, log *logger.Logger, handler Handler) error {
    err := handler(ctx)
    if err == nil {
    return nil
    }

    log.Error(ctx, "message", "ERROR", err.Error())

    // 这里判断是不是自定义错误
    if errs.IsError(err) {
    return errs.GetError(err)
    }

    return errs.Newf(errs.Unknown, errs.Unknown.String())
    }
    ```
    z1829909
        26
    z1829909  
       17 小时 27 分钟前
    每一层调用处理能处理的 error, 不能处理的加一些额外信息返回上一层
    异常其实也差不多, 只不过你自己不需要手动处理, 默认是往上抛的
    代码里尽量不要 panic, 程序启动的时候, 做一些必要的依赖检测, 如果一些关键依赖缺失或者启动异常可以 panic
    povsister
        27
    povsister  
       17 小时 23 分钟前
    和你们这些搞 java 的说不清楚.jpg
    (狗头保命
    bli22ard
        28
    bli22ard  
       17 小时 23 分钟前 via iPhone
    java 是 try catch ,只需要处理 exception 。go 里面有两种一种是 panic ,一种是 error ,panic 通过 recover 捕获,error 就是带在函数返回值里面返回。可能是最佳实战的做法是,你调用了标准里,或者第三方库,这些库返回 error 之后,你应该先用一些第三方 error 库 wrap 一下,主要目的是记录一下 error 发生的调用栈,这样上层什么位置拿到 error ,都能打印出来这个 error 是哪个位置发生的。还有一种,目的类似的做法,不记录调用栈,而改为附加一个错误码进去,这样上层的任何调用者也可以知道 error 哪里发生的,不过维护错误码这种方式维护时间越长,越容易搞混乱,导致排查问题困难。
    楼上有说,将 error 转换 panic 的做法,这样做看起来比较爽,不用多余定义 error 返回值,不用 if err!=nil 判断,只需调用入口 recover 住,但是这种做法,不是主流做法,至于为什么不主流,知道的可以解释下🤭。
    haierspi
        29
    haierspi  
       17 小时 18 分钟前
    直接 Recover 返回 API 啊
    soul11201
        30
    soul11201  
       16 小时 56 分钟前
    换个思路看下这个问题:调用栈比较深的时候,是否强制程序员显示处理流程异常终止情况。

    1. PHP 是不强制的,在调用处看不出来处理程序可能出错。如果不 Catch 一个 Exception 的基类,可能导致异常漏处理;;但是写的很多,代码就很啰嗦、过度防御 。两个方面不管从哪个角度来说,都是工程质量堪忧。

    2. Go err 最佳实践,其实就是强制显示处理错误,调用处就能看出来程序可能处理错误,整体看上去就是做 err 体操,有些呆板。

    3. Java Exception 的 Checked Exception 和 Runtime Exception 设计思路,分别是 Go err 体操路子和 PHP 的埋雷路子。Checked Exception 相比 err 体操只不过是标记在了方法签名处,看上去代码少写了两行,但 Checked Excption 是从哪里丢出来的就不太明确了。从 Checked Exception 的实际使用情况来看,偏 Runtime Exception 方向倾斜,就是逐层标记不处理的多,垃圾~

    从设计和实际使用综合来看,还是 Go Err 相对来说更严谨一点
    lysShub
        31
    lysShub  
       15 小时 35 分钟前
    @chen11 我也遇到过一个忽略 err 导致的问题,搞了整整一天
    henix
        32
    henix  
       6 小时 41 分钟前
    我的话这种情况不会使用 panic / recover ,那个是给意料之外的严重异常用的
    这种确实需要一路 return nil, err
    如果需要中间处理,那在最开始创建 err 的时候选择一个特定类型,中间件用 errors.Is 判断
    以上是如果你用网上的常用框架的话就这么做

    我个人认为这些框架的错误处理设计得不好,我开发自己的 web 项目的时候不用任何框架,只用 go 标准库
    我设计的 controller 会返回:(结果, err1, err2)
    其中 err1 代表用户输入错误,比如参数检查错误,要给用户返回 4xx
    err2 代表服务器内部错误,要给用户返回 5xx
    用这种方法,不需要 errors.Is 判断类型,只需要判断 err2 != nil 即可
    fovecifer
        33
    fovecifer  
       5 小时 8 分钟前
    我的建议是要慎重使用 panic ,切记
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2685 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 08:28 · PVG 16:28 · LAX 00:28 · JFK 03:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.