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