张子阳的博客

首页 读书 技术 店铺 关于
张子阳的博客 首页 读书 技术 关于

Go 错误处理方法集锦

2019-07-26 张子阳 分类: Go 语言

很多人,包括我自己,在刚上手Go语言的时候,对它的错误处理机制不是很熟悉,然后看到很多的方法都返回了error对象。如果直接用“_”忽略掉,很容易出现BUG,因为返回error和直接panic不同,程序并不会中断;如果逐一对error进行处理,就会有很多 if err != nil 判断,以及跟随其后的处理代码(经常是在控制台输出错误、写日志等),这样代码就会显得比较凌乱。这篇文章提供了一些处理错误的方式,以供参考借鉴。

使用panci/recover统一错误处理

情形1:返回error

当error不为nil时,手动panic,然后将处理逻辑统一放在recover中处理。

package main

import (
    "errors"
    "fmt"
)

func main() {
    err := runError1()
    fmt.Println(err)
}

func someFunc() error {
    return errors.New("方法1失败")
}

func someFunc2() error {
    return errors.New("方法2失败")
}

// 通过pannic传递错误信息 - 返回error的情况
func runError1() (err error) {
    defer func() {
        // 统一处理error的逻辑
        if r := recover(); r != nil {
            fmt.Println(r.(error).Error())
        }
    }()

    err = someFunc()
    if err != nil {
        panic(err)
    }

    err = someFunc2()
    if err != nil {
        panic(err)
    }

    return
}

情形2:不返回error

有时候,我们并不需要返回error,当有错误时,仅仅是记录一下(或者通过其他方式返回,例如在http.HandlerFunc中),那么可以这样处理:

// 通过panic传递错误信息 - 不返回error的情况
func doWork2() {
    var err error

    defer func() {
        if r := recover(); r != nil {
            fmt.Println(r.(error).Error())
        }
    }()

    err = someFunc()
    if err != nil {
        panic(err)
    }

    err = someFunc2()
    if err != nil {
        panic(err)
    }
}

情形3:使用字符串

有时候,我们需要将someFunc()返回的错误加工一下;另外,既然不需要返回error,那么就可以使用字符串:

// 通过panic传递信息 - 直接使用string
func doWork3() {
    defer func() {
        if r := recover(); r != nil {
            msg := r.(string)
            fmt.Println(msg)
        }
    }()

    err := someFunc()
    if err != nil {
        panic("1." + err.Error())
    }

    err = someFunc2()
    if err != nil {
        panic("2." + err.Error())
    }
}

情形4:使用自定义对象

有时候error包含的信息太少了,比如对于Http错误而言,我们想返回状态码和错误信息。此时可以通过自定义一个对象来完成:

// 通过panic传递信息 - 使用自定义对象
func doWork4() {
    defer func() {
        if r := recover(); r != nil {
            err := r.(*HTTPError)
            fmt.Printf("Code: %v, Message: %v\n", err.Code, err.Message)
        }
    }()

    err := someFunc()
    if err != nil {
        e := NewHTTPError(404, "PageNotFound"+err.Error())
        panic(&e)
    }

    err = someFunc2()
    if err != nil {
        e := NewHTTPError(500, "InternalServerError"+err.Error())
        panic(&e)
    }
}

// HTTPError Http错误
type HTTPError struct {
    Code    int
    Message string
}

func (x HTTPError) Error() string {
    return x.Message
}

// NewHTTPError 新建http错误
func NewHTTPError(code int, msg string) HTTPError {
    return HTTPError{Code: code, Message: msg}
}

情形5:使用自定义对象 - 返回error

当使用自定义对象时,有时候我们也想返回error,那么可以这么处理:

// 通过panic传递信息 - 使用自定义对象并返回error
func doWork5() (err error) {

    defer func() {
        if r := recover(); r != nil {
            err2 := r.(*HTTPError)
            fmt.Printf("Code: %v, Message: %v\n", err2.Code, err2.Message)
            err = err2 // 返回错误信息
        }
    }()

    err = someFunc()
    if err != nil {
        e := NewHTTPError(404, "PageNotFound"+err.Error())
        panic(&e)
    }

    err = someFunc2()
    if err != nil {
        e := NewHTTPError(500, "InternalServerError"+err.Error())
        panic(&e)
    }
    return
}

情形6:使用自定义对象 - 返回error - 通用处理函数

当defer func()中的代码越来越多时,我们会希望把其中的处理逻辑提取出来:

// 通过panic传递信息 - 返回error,自定义函数
func doWork6() (err error) {
    defer handleError(&err)

    err = someFunc()
    if err != nil {
        e := NewHTTPError(404, "PageNotFound"+err.Error())
        panic(&e)
    }

    err = someFunc2()
    if err != nil {
        e := NewHTTPError(500, "InternalServerError"+err.Error())
        panic(&e)
    }

    return
}

// 通用处理错误的方法
func handleError(err *error) {
    if r := recover(); r != nil {
        err2 := r.(*HTTPError)
        fmt.Printf("Code: %v, Message: %v\n", err2.Code, err2.Message)
        *err = err2 // 返回错误信息
    }
}
handlerError的参数需要为*error,否则err就直接成为了someFunc()的内容,而非HTTPError的错误信息。

使用defer/return统一错误处理

情形1:无返回值

有时候,我们想代码更简洁一些,直接使用 defer/return 模式来处理错误:

// 使用defer/return处理错误 - 无返回值
func doWork7() {
    var e HTTPError
    defer handleHTTPError(&e)

    err := someFunc()
    if err != nil {
        e = NewHTTPError(404, "PageNotFound"+err.Error())
        return
    }

    err = someFunc2()
    if err != nil {
        e = NewHTTPError(500, "InternalServerError"+err.Error())
        return
    }

    return
}

// 通用处理错误的方法
func handleHTTPError(e *HTTPError) {
    if e != nil {
        fmt.Printf("Code: %v, Message: %v\n", e.Code, e.Message)
    }
}

情形2:返回error

看到这里,相信对大家来说已经很容易了:

// 使用defer/return处理错误 - 有返回值
func doWork8() (err2 error) {
    var e HTTPError
    defer handleHTTPError2(&e, &err2)

    err := someFunc()
    if err != nil {
        e = NewHTTPError(404, "PageNotFound"+err.Error())
        return
    }

    err = someFunc2()
    if err != nil {
        e = NewHTTPError(500, "InternalServerError"+err.Error())
        return
    }

    return
}

// 通用处理错误的方法 - 处理返回值
func handleHTTPError2(e *HTTPError, err *error) {
    if e != nil {
        fmt.Printf("Code: %v, Message: %v\n", e.Code, e.Message)
        *err = e
    }
}

总结

这篇文章展示了在go中更优雅地处理error的多种方式...大家可以根据情况进行选择。

感谢阅读,希望这篇文章能给你带来帮助!