建设网站团队,深圳家装网站建设多少钱,最近一周的重大热点新闻,东莞今天最新消息新闻文章目录 导言一、Go 的约定二、简单错误创建1、 errors.New()2、fmt.Errorf() 三、哨兵错误四、对错误进行编程1、优雅的错误处理设计2、与错误有关的的API 五、总结 导言
在 75.错误码设计、实现统一异常处理和封装统一返回结果 中#xff0c;我们介绍了错误码的设计#… 文章目录 导言一、Go 的约定二、简单错误创建1、 errors.New()2、fmt.Errorf() 三、哨兵错误四、对错误进行编程1、优雅的错误处理设计2、与错误有关的的API 五、总结 导言
在 75.错误码设计、实现统一异常处理和封装统一返回结果 中我们介绍了错误码的设计包括错误码的分段式含义。本篇文章我们将结合错误码以及错误msg介绍一下Go中优雅的错误处理是如何设计的。共有两种常见方式
哨兵错误定义结构体至少包含code和msg)两个字段
一、Go 的约定
首先咱们需要知道 Go 语言里面有个约定就是一个方法的返回参数我们通常习惯的把错误当最后一个参数返回。
这虽然官方在这点上没有做硬性规定但是大家也都习惯这么做至于为啥 Go 要这样去设计处理异常想必是约定俗成让大家有统一的写法所以官方怎么设计咱们就怎么遵守就好了。
二、简单错误创建
Go 的标准库里面为我们提供了两种使用字符串快速创建错误的方式。
1、 errors.New()
我们可以使用errors包的 New 方法传入一个字符串快速地创建。
var e error
e errors.New(我是错误)2、fmt.Errorf()
可能大多数同学都习惯用 fmt 去拼装一些内容然后创建错误省去了用fmt.Sprintf拼装字符串然后再调用errors.New()而是拼装msg和创建错误一步到位了。
var e error
e fmt.Errorf(你好%s, 我还是错误)从这个角度可以看到其实错误对 Go 语言来说其实就是一段字符串。
三、哨兵错误
接下来我们分享 Go 中最常用的设计 error 的方式那就是哨兵模式。 哨兵错误是计算机编程中使用一个特定的值来表示不可能进一步处理的做法通常在Go语言编程中使用用于在包内先定义一些错误然后在包外进行错误值的判断。哨兵错误的注意事项在Go的官方博客中也提到哨兵错误是包级别的可以用于在包外进行错误值的判断但这样会造成包和包之间的依赖如果哨兵错误做了修改那么之前依赖该错误的所有包都需要更改。 怎么去理解呢
就像童话故事里一座城堡在城堡的一些关卡总会安排各种各样的哨兵他们不同哨兵负责的事不同。
所以我们通常会在一个包里面设置一些标志性的错误方便调用者对错误做更好的处理。
拿我们常用的 GORM 这个库吧我们在查询某条数据的时候如果没找到这条数据不知道你是怎么判断的。
其实官方为我们提供了错误哨兵在源码 github.com/jinzhu/gorm/errors.go中
var (// ErrRecordNotFound returns a record not found error. Occurs only when attempting to query the database with a struct; querying with a slice wont return this errorErrRecordNotFound errors.New(record not found)// ErrInvalidSQL occurs when you attempt a query with invalid SQLErrInvalidSQL errors.New(invalid SQL)// ErrInvalidTransaction occurs when you are trying to Commit or RollbackErrInvalidTransaction errors.New(no valid transaction)// ErrCantStartTransaction cant start transaction when you are trying to start one with BeginErrCantStartTransaction errors.New(cant start transaction)// ErrUnaddressable unaddressable valueErrUnaddressable errors.New(using unaddressable value)
)所以我们就可以直接通过返回的 error 来判断是不是没找到数据伪代码如下
g,_ : gorm.Open()
e g.Find().Error
if e gorm.ErrRecordNotFound {fmt.Println(没找到)
}其实这样用 比较是有坑的比如包内ErrRecordNotFound errors.New(record not found)错误被gorm包的开发着删除了这里的代码一升级包依赖就会编译报错了不过删除已经定义的错误这种情况一般不会出现。可以用Is解决这种情况后面会介绍。
所以如果我们在写我们的模块的时候也可以这样去设计我们的错误。
虽然这种设计模式网上也有很多人说不好因为他建立起了两个包之间的依赖说人话就是如果我们要比较错误就必须导入错误所在的包。
反正任何设计都会有人说好有人说坏大家理智看到就好了。
四、对错误进行编程
我们需要时刻记住Go 语言中错误其实就是一串字符串。
我们应该尽量避免去比较 error.Error() 输出的值因为不同协作者写代码时返回的字符串错误提示信息很可能是不一样的。
Go 语言中的错误定义是一个接口只要是声明了 Error() string 这个方法就意味着它实现了Error接口。
这是 Go 中的错误定义源码
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {Error() string
}如果官方的错误并不能满足你的需求咱们也可以自定义。
1、优雅的错误处理设计
我们先来使用常量去创建自定义错误吧
type MyError stringfunc (this MyError) Error() string {return string(this)
}这样我们就创建好我们的自定义错误了使用下
func main() {var e errore MyError(hello)fmt.Println(e)
}当然我们可以把 string 换成 struct 同时加入很多我们自定义的属性工作中非常常用
type MyError struct {Code intMsg string
}func NewMyError(code int, msg string) *MyError {return MyError{Code: code, Msg: msg}
}func (this MyError) Error() string {return fmt.Sprintf(%d-%s,this.Code, this.Msg)
}// FindUser 模拟下我们的业务方法
func FindUser() error {return NewMyError(404, 找不到内容)
}func main() {var e errore FindUser()fmt.Println(e)
}当然实际工作中一般还会用metrics打点记录错误情况方便后续进行监控报警而业务上有一些预期内的错误或者稳定性不需要处理的错误我们希望不要报警。一方面可以在报警规则中过滤另一方面我们其实可以在错误设计和上报的时候就打点标记错误是否为稳定性错误。如下
// 错误码枚举
type ErrorCodeEnum int64const (// 定义可预见的异常UserNotFound ErrCodeEnum 10001PasswrodErr ErrCodeEnum 10002
)type BizErr struct {code ErrCodeEnum msg stringisStable bool // 是否是稳定性错误
}func NewBizErr(code ErrCodeEnum, msg string) *OpStrategyErr {return OpStrategyErr{code: code,msg: msg,isStable: ErrorCodeIsStable(code),}
}func (e *OpStrategyErr) Code() strategy_error.OpStrategyErrCode {return e.code
}func (e *OpStrategyErr) Error() string {return e.msg
}func (e *OpStrategyErr) IsStable() bool {return e.isStable
}func ErrorCodeIsStable(errCode ErrCodeEnum) bool {errCodeStr : strconv.FormatInt(int64(errCode), 10)if len(errCodeStr) 4 {return false}// 假设以1000开头认为是稳定性错误if errCodeStr[:4] 1000 {return true}return false
}func IsErrNil(err error) bool {if err nil {return true}vi : reflect.ValueOf(err)if vi.Kind() reflect.Ptr {return vi.IsNil()}return false
}
2、与错误有关的的API
最后我们来说说 Go 语言中与错误有关的 API到目前为止我们面对错误除了输出外就是使用 对错误进行哨兵比较但是这样未必准确。
所以官方在错误的基础上又扩展了几个 API。
1、Is
我们面对错误尽量不要使用这样的方式去比较
// 尽量少用
if e.Error() 404-找不到内容 {
}尽量少用最好不用。
也少用这样的方式
type MyError struct {Code intMsg string
}func NewMyError(code int, msg string) *MyError {return MyError{Code: code, Msg: msg}
}func (this MyError) Error() string {return fmt.Sprintf(%d-%s,this.Code, this.Msg)
}var ErrorNotFind NewMyError(404, 找不到内容)// FindUser 模拟下我们的业务方法
func FindUser() error {return ErrorNotFind
}func main() {var e errore FindUser()log.Println(e)// 尽量少用if e ErrorNotFind {}
}目前我们的错误结构体还是非常简单的如果我们的结构体里面的属性再多几个很可能就会出现牛头对马嘴情况。
注意Go 中 struct是值类型所以比较是比较里面每个字段的值是否相等
所以官方为我们提供了 Is 方法的 API他默认使用将特定的错误与错误链中的错误进行比较如果不一样就会去调用错误实现的Is方法进行比较。
func (this *MyError) Is(target error) bool {log.Println(到这里来了....)if inputE, ok : target.(*MyError); ok {// 注意 比较就是比较struct的各字段是否相等而我们这里用Is只想比较错误码是否相等//if inputE.Code this.Code inputE.Msg this.Msg {// return true//}if inputE.Code this.Code {return true}}return false
}func main() {var e errore FindUser()log.Println(e)// 注意我们定义的时候是var ErrorNotFind NewMyError(404, 找不到内容)// 但这里传递的msg是ddd所以用eNewMyError(404, ddd)会是falseif errors.Is(e, NewMyError(404, ddd)) {log.Println(是 ErrorNotFind)}else {log.Println(不是 ErrorNotFind)}
}首先我们先去实现下 Is 这个方法随后我们使用 errors.Is 进行比较你会看到控制台输出了
$ go run main.go
2024/02/07 14:20:48 404-找不到内容
2024/02/07 14:20:48 到这里来了....
2024/02/07 14:20:48 是 ErrorNotFind从输出结果不难看出errors.Is也是先用进行了比较发现不相等后又调用了MyError的Is方法只比较错误码是否一致是相等的。
2、Unwrap
这是一个不大常用的API标准库里面fmt.Errorf就是一个非常典型的使用案例。
场景是什么呢
我们通常在错误异常的时候会有给错误加上一些上下文的需求那在哪里加呢
就是错误的 Unwrap 方法里面
func (this *MyError) Unwrap() error {this.Msg hello this.Msgreturn this
}func main() {var e errore FindUser()log.Println(最原始的错误, e)wE : errors.Unwrap(e)log.Println(加了上下文的错误, wE)
}然后看下我们的输出结果
$ go run main.go
2024/02/07 14:30:06 最原始的错误 404-找不到内容
2024/02/07 14:30:06 加了上下文的错误 404-hello找不到内容你会发现errors.Unwrap 后的错误调用了我们自定义错误的 Unwrap 方法在我们的 msg 前面加了 hello。
对错误进行编程最常用的两个API就是这两个了还有一些不大常用的比如 As大家感兴趣的可以自行去翻阅下资料。
五、总结
Go 的错误处理和其他语言不太一样如果遵守错误处理的规范不对错误进行隐藏写出来的代码一般都是比较健壮的。
于是就难免会出现一个包里面特别多的错误处理代码这就是时间和空间的博弈就看 Go 语言的领路人如何取舍了。
其次每个人对错误的理解和处理思路方式都不太一样。就比如我们到底是该多使用哨兵错误还是该少用呢
但是最通用的还是哨兵错误以及自定义结构体作为错误。 文章转载自: http://www.morning.fjptn.cn.gov.cn.fjptn.cn http://www.morning.hsxkq.cn.gov.cn.hsxkq.cn http://www.morning.ccsdx.cn.gov.cn.ccsdx.cn http://www.morning.wfzdh.cn.gov.cn.wfzdh.cn http://www.morning.cwskn.cn.gov.cn.cwskn.cn http://www.morning.kcrw.cn.gov.cn.kcrw.cn http://www.morning.wsnjn.cn.gov.cn.wsnjn.cn http://www.morning.lwhsp.cn.gov.cn.lwhsp.cn http://www.morning.crqpl.cn.gov.cn.crqpl.cn http://www.morning.fndmk.cn.gov.cn.fndmk.cn http://www.morning.hqbk.cn.gov.cn.hqbk.cn http://www.morning.rgxcd.cn.gov.cn.rgxcd.cn http://www.morning.bmfqg.cn.gov.cn.bmfqg.cn http://www.morning.xsklp.cn.gov.cn.xsklp.cn http://www.morning.qghjc.cn.gov.cn.qghjc.cn http://www.morning.cknws.cn.gov.cn.cknws.cn http://www.morning.jlxld.cn.gov.cn.jlxld.cn http://www.morning.mrskk.cn.gov.cn.mrskk.cn http://www.morning.datadragon-auh.cn.gov.cn.datadragon-auh.cn http://www.morning.mzcsp.cn.gov.cn.mzcsp.cn http://www.morning.qyxwy.cn.gov.cn.qyxwy.cn http://www.morning.wqbrg.cn.gov.cn.wqbrg.cn http://www.morning.qdrrh.cn.gov.cn.qdrrh.cn http://www.morning.ytmx.cn.gov.cn.ytmx.cn http://www.morning.rqbkc.cn.gov.cn.rqbkc.cn http://www.morning.hxljc.cn.gov.cn.hxljc.cn http://www.morning.skrww.cn.gov.cn.skrww.cn http://www.morning.dzqr.cn.gov.cn.dzqr.cn http://www.morning.xwlmr.cn.gov.cn.xwlmr.cn http://www.morning.wtnwf.cn.gov.cn.wtnwf.cn http://www.morning.gwsll.cn.gov.cn.gwsll.cn http://www.morning.hphqy.cn.gov.cn.hphqy.cn http://www.morning.tzzxs.cn.gov.cn.tzzxs.cn http://www.morning.jlpdc.cn.gov.cn.jlpdc.cn http://www.morning.wpspf.cn.gov.cn.wpspf.cn http://www.morning.yslfn.cn.gov.cn.yslfn.cn http://www.morning.qwzpd.cn.gov.cn.qwzpd.cn http://www.morning.cywf.cn.gov.cn.cywf.cn http://www.morning.yrxcn.cn.gov.cn.yrxcn.cn http://www.morning.wknbc.cn.gov.cn.wknbc.cn http://www.morning.fjlsfs.com.gov.cn.fjlsfs.com http://www.morning.psxcr.cn.gov.cn.psxcr.cn http://www.morning.crdtx.cn.gov.cn.crdtx.cn http://www.morning.dndk.cn.gov.cn.dndk.cn http://www.morning.prgrh.cn.gov.cn.prgrh.cn http://www.morning.prls.cn.gov.cn.prls.cn http://www.morning.rkfxc.cn.gov.cn.rkfxc.cn http://www.morning.ypcbm.cn.gov.cn.ypcbm.cn http://www.morning.lbqt.cn.gov.cn.lbqt.cn http://www.morning.ktnmg.cn.gov.cn.ktnmg.cn http://www.morning.fjshyc.com.gov.cn.fjshyc.com http://www.morning.smspc.cn.gov.cn.smspc.cn http://www.morning.rnnq.cn.gov.cn.rnnq.cn http://www.morning.zkdmk.cn.gov.cn.zkdmk.cn http://www.morning.nwbnt.cn.gov.cn.nwbnt.cn http://www.morning.frpfk.cn.gov.cn.frpfk.cn http://www.morning.ldcrh.cn.gov.cn.ldcrh.cn http://www.morning.stbfy.cn.gov.cn.stbfy.cn http://www.morning.linzhigongmao.cn.gov.cn.linzhigongmao.cn http://www.morning.gyylt.cn.gov.cn.gyylt.cn http://www.morning.hbxnb.cn.gov.cn.hbxnb.cn http://www.morning.dgsx.cn.gov.cn.dgsx.cn http://www.morning.hgbzc.cn.gov.cn.hgbzc.cn http://www.morning.fbrshjf.com.gov.cn.fbrshjf.com http://www.morning.yfqhc.cn.gov.cn.yfqhc.cn http://www.morning.qkgwx.cn.gov.cn.qkgwx.cn http://www.morning.trplf.cn.gov.cn.trplf.cn http://www.morning.xkwyk.cn.gov.cn.xkwyk.cn http://www.morning.ybnps.cn.gov.cn.ybnps.cn http://www.morning.qbzdj.cn.gov.cn.qbzdj.cn http://www.morning.cxtbh.cn.gov.cn.cxtbh.cn http://www.morning.hhskr.cn.gov.cn.hhskr.cn http://www.morning.gqtw.cn.gov.cn.gqtw.cn http://www.morning.smrty.cn.gov.cn.smrty.cn http://www.morning.tbjtm.cn.gov.cn.tbjtm.cn http://www.morning.fflnw.cn.gov.cn.fflnw.cn http://www.morning.tpyjr.cn.gov.cn.tpyjr.cn http://www.morning.rkzk.cn.gov.cn.rkzk.cn http://www.morning.ptmgq.cn.gov.cn.ptmgq.cn http://www.morning.yqwrj.cn.gov.cn.yqwrj.cn