错误处理-错误比较

Equal 比较方法

错误对象支持比较, Equal 方法用于完整判断两个 error 是否相同。主要通过以下方法:

// Equal reports whether current error `err` equals to error `target`.
// Please note that, in default comparison for `Error`,
// the errors are considered the same if both the `code` and `text` of them are the same.
func Equal(err, target error) bool

接口定义

如果自定义的错误数据结构需要支持比较,需要自定义的错误结构实现以下接口:

// IEqual is the interface for Equal feature.
type IEqual interface {
    Error() string
    Equal(target error) bool
}

通过 GoFrame 框架错误组件创建的错误对象已经实现实现了该接口,组件默认比较逻辑判断错误码和错误信息。

错误处理-错误比较 - 图1 信息

需要注意,如果两个错误均不带错误码,并且错误信息相等,那么组件认为两个错误相等。

使用示例

func ExampleEqual() {
    err1 := errors.New("permission denied")
    err2 := gerror.New("permission denied")
    err3 := gerror.NewCode(gcode.CodeNotAuthorized, "permission denied")
    fmt.Println(gerror.Equal(err1, err2))
    fmt.Println(gerror.Equal(err2, err3))

    // Output:
    // true
    // false
}

Is 判断错误链中是否包含目标错误

  • 说明:Is 方法用于判断错误链中是否包含特定的目标错误。该方法是对 Go 标准库 errors.Is 的封装,保证了与标准库相同的性能。

  • 格式:

Is(err, target error) bool
  • 示例:
func ExampleIs() {
    // 创建一个特定的错误作为目标
    targetErr := gerror.New("target error")

    // 包装错误
    err1 := gerror.Wrap(targetErr, "wrap error 1")
    err2 := gerror.Wrap(err1, "wrap error 2")

    // 使用Is判断是否包含目标错误
    fmt.Println(gerror.Is(err2, targetErr))

    // 与不相关的错误进行判断
    otherErr := gerror.New("other error")
    fmt.Println(gerror.Is(err2, otherErr))

    // Output:
    // true
    // false
}

As 将错误转换为特定类型

  • 说明:As 方法用于将错误链中的错误转换为特定类型。它会在错误链中查找第一个与目标类型匹配 的错误,如果找到,则将其转换为目标类型并返回 true。该方法是对 Go 标准库 errors.As 的封装,保证了与标准库相同的性能。

  • 格式:

As(err error, target any) bool
  • 示例:
// 自定义错误类型
type MyError struct {
    Message string
    Code    int
}

// 实现error接口
func (e *MyError) Error() string {
    return fmt.Sprintf("code: %d, message: %s", e.Code, e.Message)
}

func ExampleAs() {
    // 创建一个自定义错误
    originalErr := &MyError{Message: "database connection failed", Code: 500}

    // 包装错误
    err := gerror.Wrap(originalErr, "operation failed")

    // 尝试将错误转换为MyError类型
    var myErr *MyError
    if gerror.As(err, &myErr) {
        fmt.Println("Error code:", myErr.Code)
        fmt.Println("Error message:", myErr.Message)
    }

    // 尝试转换为不匹配的类型
    type OtherError struct{}
    var otherErr *OtherError
    fmt.Println(gerror.As(err, &otherErr))

    // Output:
    // Error code: 500
    // Error message: database connection failed
    // false
}

As 方法在以下场景中特别有用:

  1. 类型断言 :当您需要从错误链中提取特定类型的错误信息时。

  2. 错误处理 :根据错误类型执行不同的处理逻辑。

  3. 自定义错误恢复 :当需要从错误中提取特定信息来实现错误恢复机制时。

下面是一个实际应用的例子:

package main

import (
    "fmt"

    "github.com/gogf/gf/v2/errors/gerror"
)

// 数据库错误
type DBError struct {
    Table   string
    Message string
}

func (e *DBError) Error() string {
    return fmt.Sprintf("database error on table '%s': %s", e.Table, e.Message)
}

// 认证错误
type AuthError struct {
    Username string
    Message  string
}

func (e *AuthError) Error() string {
    return fmt.Sprintf("authentication error for user '%s': %s", e.Username, e.Message)
}

// 模拟数据库操作
func queryDatabase() error {
    return gerror.Wrap(&DBError{
        Table:   "users",
        Message: "connection timeout",
    }, "database query failed")
}

// 模拟用户认证
func authenticateUser() error {
    return gerror.Wrap(&AuthError{
        Username: "john_doe",
        Message:  "invalid password",
    }, "authentication failed")
}

// 处理不同类型的错误
func handleError(err error) {
    // 尝试将错误转换为数据库错误
    var dbErr *DBError
    if gerror.As(err, &dbErr) {
        fmt.Println("Database error detected:")
        fmt.Println("  Table:", dbErr.Table)
        fmt.Println("  Message:", dbErr.Message)
        fmt.Println("  Recommended action: Check database connection and retry")
        return
    }

    // 尝试将错误转换为认证错误
    var authErr *AuthError
    if gerror.As(err, &authErr) {
        fmt.Println("Authentication error detected:")
        fmt.Println("  Username:", authErr.Username)
        fmt.Println("  Message:", authErr.Message)
        fmt.Println("  Recommended action: Verify credentials and try again")
        return
    }

    // 其他错误类型
    fmt.Println("Unknown error:", err.Error())
}

func main() {
    // 模拟数据库错误
    err1 := queryDatabase()
    handleError(err1)

    fmt.Println()

    // 模拟认证错误
    err2 := authenticateUser()
    handleError(err2)
}

运行结果:

Database error detected:
  Table: users
  Message: connection timeout
  Recommended action: Check database connection and retry

Authentication error detected:
  Username: john_doe
  Message: invalid password
  Recommended action: Verify credentials and try again