Go语言主要的设计准则是:简洁、明白,简洁是指语法和C类似,相当的简单,明白是指任何语句都是很明显的,不含有任何隐含的东西,在错误处理方案的设计中也贯彻了这一思想。我们知道在C语言里面是通过返回-1或者NULL之类的信息来表示错误,但是对于使用者来说,不查看相应的API说明文档,根本搞不清楚这个返回值究竟代表什么意思,比如:返回0是成功,还是失败,而Go定义了一个叫做error的类型,来显式表达错误。在使用时,通过把返回的error变量与nil的比较,来判定操作是否成功。例如os.Open函数在打开文件失败时将返回一个不为nil的error变量

func Open(name string) (file *File, err error)

下面这个例子通过调用os.Open打开一个文件,如果出现错误,那么就会调用log.Fatal来输出错误信息:

f, err := os.Open("filename.ext")
if err != nil {log.Fatal(err)
}

类似于os.Open函数,标准包中所有可能出错的API都会返回一个error变量,以方便错误处理,这个小节将详细地介绍error类型的设计,和讨论开发Web应用中如何更好地处理error。

Error类型

error类型是一个接口类型,这是它的定义:

type error interface {Error() string
}

error是一个内置的接口类型,我们可以在/builtin/包下面找到相应的定义。而我们在很多内部包里面用到的 error是errors包下面的实现的私有结构errorString

// errorString is a trivial implementation of error.
type errorString struct {s string
}func (e *errorString) Error() string {return e.s
}

你可以通过errors.New把一个字符串转化为errorString,以得到一个满足接口error的对象,其内部实现如下:

// New returns an error that formats as the given text.
func New(text string) error {return &errorString{text}
}

下面这个例子演示了如何使用errors.New:

func Sqrt(f float64) (float64, error) {if f < 0 {return 0, errors.New("math: square root of negative number")}// implementation
}

在下面的例子中,我们在调用Sqrt的时候传递的一个负数,然后就得到了non-nil的error对象,将此对象与nil比较,结果为true,所以fmt.Println(fmt包在处理error时会调用Error方法)被调用,以输出错误,请看下面调用的示例代码:

f, err := Sqrt(-1)
if err != nil {fmt.Println(err)
}

自定义Error

通过上面的介绍我们知道error是一个interface,所以在实现自己的包的时候,通过定义实现此接口的结构,我们就可以实现自己的错误定义,请看来自Json包的示例:

type SyntaxError struct {msg    string // 错误描述Offset int64  // 错误发生的位置
}func (e *SyntaxError) Error() string { return e.msg }

Offset字段在调用Error的时候不会被打印,但是我们可以通过类型断言获取错误类型,然后可以打印相应的错误信息,请看下面的例子:

if err := dec.Decode(&val); err != nil {if serr, ok := err.(*json.SyntaxError); ok {line, col := findLine(f, serr.Offset)return fmt.Errorf("%s:%d:%d: %v", f.Name(), line, col, err)}return err
}

需要注意的是,函数返回自定义错误时,返回值推荐设置为error类型,而非自定义错误类型,特别需要注意的是不应预声明自定义错误类型的变量。例如:

func Decode() *SyntaxError { // 错误,将可能导致上层调用者err!=nil的判断永远为true。var err *SyntaxError     // 预声明错误变量if 出错条件 {err = &SyntaxError{}}return err               // 错误,err永远等于非nil,导致上层调用者err!=nil的判断始终为true
}

原因见 http://golang.org/doc/faq#nil_error

上面例子简单的演示了如何自定义Error类型。但是如果我们还需要更复杂的错误处理呢?此时,我们来参考一下net包采用的方法:

package nettype Error interface {errorTimeout() bool   // Is the error a timeout?Temporary() bool // Is the error temporary?
}

在调用的地方,通过类型断言err是不是net.Error,来细化错误的处理,例如下面的例子,如果一个网络发生临时性错误,那么将会sleep 1秒之后重试:

if nerr, ok := err.(net.Error); ok && nerr.Temporary() {time.Sleep(1e9)continue
}
if err != nil {log.Fatal(err)
}

错误处理

Go在错误处理上采用了与C类似的检查返回值的方式,而不是其他多数主流语言采用的异常方式,这造成了代码编写上的一个很大的缺点:错误处理代码的冗余,对于这种情况是我们通过复用检测函数来减少类似的代码。

请看下面这个例子代码:

func init() {http.HandleFunc("/view", viewRecord)
}func viewRecord(w http.ResponseWriter, r *http.Request) {c := appengine.NewContext(r)key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)record := new(Record)if err := datastore.Get(c, key, record); err != nil {http.Error(w, err.Error(), 500)return}if err := viewTemplate.Execute(w, record); err != nil {http.Error(w, err.Error(), 500)}
}

上面的例子中获取数据和模板展示调用时都有检测错误,当有错误发生时,调用了统一的处理函数http.Error,返回给客户端500错误码,并显示相应的错误数据。但是当越来越多的HandleFunc加入之后,这样的错误处理逻辑代码就会越来越多,其实我们可以通过自定义路由器来缩减代码(实现的思路可以参考第三章的HTTP详解)。

type appHandler func(http.ResponseWriter, *http.Request) errorfunc (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {if err := fn(w, r); err != nil {http.Error(w, err.Error(), 500)}
}

上面我们定义了自定义的路由器,然后我们可以通过如下方式来注册函数:

func init() {http.Handle("/view", appHandler(viewRecord))
}

当请求/view的时候我们的逻辑处理可以变成如下代码,和第一种实现方式相比较已经简单了很多。

func viewRecord(w http.ResponseWriter, r *http.Request) error {c := appengine.NewContext(r)key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)record := new(Record)if err := datastore.Get(c, key, record); err != nil {return err}return viewTemplate.Execute(w, record)
}

上面的例子错误处理的时候所有的错误返回给用户的都是500错误码,然后打印出来相应的错误代码,其实我们可以把这个错误信息定义的更加友好,调试的时候也方便定位问题,我们可以自定义返回的错误类型:

type appError struct {Error   errorMessage stringCode    int
}

这样我们的自定义路由器可以改成如下方式:

type appHandler func(http.ResponseWriter, *http.Request) *appErrorfunc (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {if e := fn(w, r); e != nil { // e is *appError, not os.Error.c := appengine.NewContext(r)c.Errorf("%v", e.Error)http.Error(w, e.Message, e.Code)}
}

这样修改完自定义错误之后,我们的逻辑处理可以改成如下方式:

func viewRecord(w http.ResponseWriter, r *http.Request) *appError {c := appengine.NewContext(r)key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)record := new(Record)if err := datastore.Get(c, key, record); err != nil {return &appError{err, "Record not found", 404}}if err := viewTemplate.Execute(w, record); err != nil {return &appError{err, "Can't display record", 500}}return nil
}

如上所示,在我们访问view的时候可以根据不同的情况获取不同的错误码和错误信息,虽然这个和第一个版本的代码量差不多,但是这个显示的错误更加明显,提示的错误信息更加友好,扩展性也比第一个更好。

golang错误处理相关推荐

  1. Golang错误和异常处理的正确姿势

    Golang错误和异常处理的正确姿势 错误和异常是两个不同的概念,非常容易混淆.很多程序员习惯将一切非正常情况都看做错误,而不区分错误和异常,即使程序中可能有异常抛出,也将异常及时捕获并转换成错误.从 ...

  2. Golang 错误处理机制详解

    本文介绍Golang错误处理机制,包括不同类型错误处理.定义运行时错误等内容. golang错误处理机制 Go错误处理类似C语言,没有提供任何异常,以及类java语言使用的try/catch异常处理机 ...

  3. Golang 错误捕获 Panic 与 Recover

    Golang 错误捕获 Panic 与 Recover,我抓住你了,Error Golang轻松学习 文章目录 Golang 错误捕获 Panic 与 Recover,我抓住你了,Error 一.Go ...

  4. Golang错误处理机制

    基础 错误处理应该时工程的一部分,Golang中的error是一个interface类型,如下: type error interface {Error() string } 凡是实现Error()方 ...

  5. 爆肝3天只为Golang 错误处理最佳实践

    对于开发者来说,要是不爽Go错误处理,那就看看最佳实践.Go可能引入try catch吗?那可能估计有点难度.本文简单介绍Go为什么选择这样的错误处理和目前常见处理方式,并梳理常见Go错误处理痛点,给 ...

  6. golang错误处理(实验楼)

    Go中有一个名为error的内置接口,定义如下: type error interface { Error() string }   Go是使用一个独立的·明确的返回值来传递错误信息的.这与使用异常的 ...

  7. 【GoLang】GoLang 错误处理 -- 异常处理思路示例

    代码: package mainimport ("fmt"// "testing" )var Pkg = "packageName"type ...

  8. golang错误处理机制(异常处理)

    看一段代码,引入错误处理 对上面代码的总结: 在默认情况下,当发生错误后(panic) ,程序就会退出(崩溃.) 如果我们希望:当发生错误后,可以捕获到错误,并进行处理,保证程序可以继续执行.还可 以 ...

  9. golang错误:The process cannot access the file because it is being used by another process

    情况一,创建文件后再删除: func main() {NewConfigIni()DeleteConfigTemp() }func DeleteConfigTemp() {path := " ...

最新文章

  1. java xml dom4j 解析_在JAVA中怎么用DOM和DOM4j解析XML啊?
  2. ubuntu16.04在英文状态下安装中文语言包的过程(法一:图形界面的方式) 以及 安装中文语言包后无法选择汉语问题的解决
  3. python dbscan 如何确定eps参数_如何选择eps和minPts(DBSCAN算法的两个参数)以获得有效结果?...
  4. 在线教育音视频质量评价与感知系统
  5. linux msgctl函数,msgctl()函数
  6. 快速掌握用python处理Excel
  7. [常用知识]如何在Eclipse、myEclipse中分别配置Tomcat和JBoss应用服务器
  8. Modbus协议使用常见问题分析
  9. 95-138-010-源码-Function-KeyedProcessFunction
  10. Java web 中的 三层架构
  11. 一个人学的软件测试,到底有多难?
  12. paip.验证码识别---序列号的反转
  13. 【问链财经-区块链基础知识系列】 第十六课 区块链将变革教育产业,未来有六大应用方向
  14. FIT2CLOUD云管平台完成华为云鲲鹏云服务兼容性认证
  15. 超感光徕卡电影四摄:华为Mate30系列国内开售
  16. OpenCV实现人脸对齐
  17. 如何自定义Chromecast的背景以显示个性化图片,新闻等
  18. 《精益数据分析》:网易创始人丁磊力荐
  19. 会员管理系统,建议收藏!
  20. 微信小程序带图片弹窗简单实现

热门文章

  1. 【Flutter】StatefulWidget 组件 ( 创建 StatefulWidget 组件 | MaterialApp 组件 | Scaffold 组件 )
  2. vue单选,多选,多选的内容显示在页面可删除
  3. 结合自己造的轮子实践按需加载
  4. ASP.NET Core 中文文档 第四章 MVC(2.2)模型验证
  5. [C#1] 10-事件
  6. jstl:sql标签介绍
  7. 《Windows Communication Foundation之旅》系列之一
  8. 标号的类型是near还是far有什么区别,作用是什么?
  9. 【python3】爬取鼠绘汉化的海贼王漫画
  10. 使用栈结构完毕四则运算