Go语言自定义错误类型分类与分组处理的标准模式探讨
Go 错误分组的标准实现方案
你不需要依赖带冗余空方法的接口来实现错误分组,Go社区有几种更实用的标准模式可以满足你的需求——区分可对外暴露的友好错误和仅内部记录的错误:
模式1:带分组标识的接口(最直观)
定义一个包含实际用途方法的接口替代空方法,比如返回错误分组类型的Group()方法,既实现分组,又能明确错误类别:
// ErrorGroup 定义错误分组接口,嵌入error接口无需重复实现Error() type ErrorGroup interface { error Group() string // 返回分组标识,如"friendly"或"internal" } // MyFriendlyError 自定义友好错误,可对外暴露详情 type MyFriendlyError struct { Msg string } func (e MyFriendlyError) Error() string { return e.Msg } func (e MyFriendlyError) Group() string { return "friendly" } // InternalDBError 自定义内部错误,仅内部记录详情 type InternalDBError struct { Msg string Code int Trace string } func (e InternalDBError) Error() string { return e.Msg // 对外返回通用信息 } func (e InternalDBError) Group() string { return "internal" }
使用方式
通过errors.As匹配接口,按分组处理:
err := DoSomething() if err != nil { var eg ErrorGroup if errors.As(err, &eg) { switch eg.Group() { case "friendly": fmt.Println(err.Error()) // 直接返回给调用方 case "internal": // 内部日志记录详细信息 if dbErr, ok := eg.(InternalDBError); ok { log.Printf("internal error trace: %s, code: %d", dbErr.Trace, dbErr.Code) } fmt.Println("服务暂时不可用,请稍后重试") // 返回通用提示 } } else { fmt.Println("未知错误") } }
模式2:基础结构体嵌入(无额外方法)
利用Go的结构体嵌入特性,定义基础分组结构体,具体错误类型嵌入该结构体,通过errors.As匹配基础类型实现分组:
// FriendlyError 友好错误分组的基础结构体 type FriendlyError struct { Msg string } func (e FriendlyError) Error() string { return e.Msg } // UserNotFoundError 具体友好错误,嵌入基础分组结构体 type UserNotFoundError struct { FriendlyError UserID int } // InternalError 内部错误分组的基础结构体 type InternalError struct { Msg string Detail string // 内部详情字段 } func (e InternalError) Error() string { return "服务暂时不可用" // 对外返回通用信息 } // DBConnectError 具体内部错误,嵌入基础分组结构体 type DBConnectError struct { InternalError DSN string }
使用方式
通过errors.As匹配基础分组结构体,判断所属分组:
err := DoSomething() if err != nil { var fe FriendlyError if errors.As(err, &fe) { fmt.Println(fe.Msg) // 返回友好错误信息 return } var ie InternalError if errors.As(err, &ie) { // 内部记录详细信息 if dbErr, ok := err.(*DBConnectError); ok { log.Printf("DB connect failed: %s, DSN: %s", ie.Detail, dbErr.DSN) } fmt.Println(ie.Error()) // 返回通用提示 return } fmt.Println("未知错误") }
模式3:哨兵错误+Is方法(适合简单分组)
如果分组类型较少,可以定义分组哨兵错误,让具体错误实现Is方法关联到哨兵,通过errors.Is判断分组:
// 定义分组哨兵错误 var ErrFriendlyGroup = errors.New("friendly error group") var ErrInternalGroup = errors.New("internal error group") // UserNotFoundError 友好错误,关联到友好分组哨兵 type UserNotFoundError struct { UserID int } func (e UserNotFoundError) Error() string { return fmt.Sprintf("用户%d不存在", e.UserID) } func (e UserNotFoundError) Is(target error) bool { return target == ErrFriendlyGroup } // DBError 内部错误,关联到内部分组哨兵 type DBError struct { Msg string } func (e DBError) Error() string { return "服务暂时不可用" } func (e DBError) Is(target error) bool { return target == ErrInternalGroup }
使用方式
通过errors.Is判断所属分组:
err := DoSomething() if err != nil { if errors.Is(err, ErrFriendlyGroup) { fmt.Println(err.Error()) } else if errors.Is(err, ErrInternalGroup) { log.Printf("内部错误详情: %v", err) fmt.Println(err.Error()) } else { fmt.Println("未知错误") } }
内容的提问来源于stack exchange,提问作者Joe Enos
相关产品推荐
相关产品推荐

