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的多种方式...大家可以根据情况进行选择。
感谢阅读,希望这篇文章能给你带来帮助!