You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 09:05:21