Skip to content
鼓励作者:欢迎打赏犒劳

go自定义异常

text
your-project/
├── errors/
│   ├── errors.go          # 错误定义和核心结构
│   ├── predefined.go      # 预定义错误
│   ├── utils.go           # 工具函数
├── go.mod
└── main.go

errors.go

go
package errors

import (
	"fmt"
)

// CustomError 自定义错误类型
type CustomError struct {
	HTTPCode int    `json:"-"`                 // HTTP 状态码,不序列化到 JSON
	Code     int    `json:"code"`              // 业务错误码
	Message  string `json:"message"`           // 错误信息
	Details  string `json:"details,omitempty"` // 错误详情(可选)
	Err      error  `json:"-"`                 // 原始错误(用于日志,不序列化到 JSON)
}

// Error 实现 error 接口
func (e *CustomError) Error() string {
	if e.Err != nil {
		return fmt.Sprintf("HTTPCode: %d, Code: %d, Message: %s, Original: %v",
			e.HTTPCode, e.Code, e.Message, e.Err)
	}
	return fmt.Sprintf("HTTPCode: %d, Code: %d, Message: %s",
		e.HTTPCode, e.Code, e.Message)
}

// Unwrap 实现 errors.Unwrap 接口,便于错误链处理
func (e *CustomError) Unwrap() error {
	return e.Err
}

// WithDetails 添加错误详情
func (e *CustomError) WithDetails(details string) *CustomError {
	e.Details = details
	return e
}

// WithError 添加原始错误
func (e *CustomError) WithError(err error) *CustomError {
	e.Err = err
	return e
}

// New 创建新的自定义错误
func New(httpCode, code int, message string) *CustomError {
	return &CustomError{
		HTTPCode: httpCode,
		Code:     code,
		Message:  message,
	}
}

predefined.go

go
package errors

import "net/http"

// 通用错误
var (
	ErrBadRequest         = New(http.StatusBadRequest, 40000, "请求参数错误")
	ErrUnauthorized       = New(http.StatusUnauthorized, 40100, "未授权访问")
	ErrForbidden          = New(http.StatusForbidden, 40300, "禁止访问")
	ErrNotFound           = New(http.StatusNotFound, 40400, "资源不存在")
	ErrInternalServer     = New(http.StatusInternalServerError, 50000, "服务器内部错误")
	ErrServiceUnavailable = New(http.StatusServiceUnavailable, 50300, "服务暂时不可用")
)

// 业务相关错误
var (
	ErrUserNotFound     = New(http.StatusNotFound, 40401, "用户不存在")
	ErrUserExists       = New(http.StatusBadRequest, 40001, "用户已存在")
	ErrInvalidPassword  = New(http.StatusBadRequest, 40002, "密码错误")
	ErrTokenExpired     = New(http.StatusUnauthorized, 40101, "令牌已过期")
	ErrTokenInvalid     = New(http.StatusUnauthorized, 40102, "无效令牌")
	ErrPermissionDenied = New(http.StatusForbidden, 40301, "权限不足")
)

// 数据库相关错误
var (
	ErrDBQuery  = New(http.StatusInternalServerError, 50001, "数据库查询失败")
	ErrDBInsert = New(http.StatusInternalServerError, 50002, "数据库插入失败")
	ErrDBUpdate = New(http.StatusInternalServerError, 50003, "数据库更新失败")
	ErrDBDelete = New(http.StatusInternalServerError, 50004, "数据库删除失败")
	ErrDBConn   = New(http.StatusInternalServerError, 50005, "数据库连接失败")
)

// 文件操作相关错误
var (
	ErrFileNotFound    = New(http.StatusNotFound, 40402, "文件不存在")
	ErrFileRead        = New(http.StatusInternalServerError, 50006, "文件读取失败")
	ErrFileWrite       = New(http.StatusInternalServerError, 50007, "文件写入失败")
	ErrFileSizeExceed  = New(http.StatusBadRequest, 40003, "文件大小超出限制")
	ErrFileTypeInvalid = New(http.StatusBadRequest, 40004, "文件类型不支持")
)

utils.go

go
package errors

import (
	"encoding/json"
	"log"
	"net/http"
)

// IsCustomError 判断是否为自定义错误
func IsCustomError(err error) bool {
	_, ok := err.(*CustomError)
	return ok
}

// FromError 从标准 error 转换,如果不是 CustomError 则返回内部服务器错误
func FromError(err error) *CustomError {
	if customErr, ok := err.(*CustomError); ok {
		return customErr
	}
	return ErrInternalServer.WithError(err).WithDetails(err.Error())
}

// ToJSON 将错误转换为 JSON 字符串
func (e *CustomError) ToJSON() string {
	jsonBytes, err := json.Marshal(e)
	if err != nil {
		log.Printf("序列化错误失败: %v", err)
		return `{"code":50000,"message":"序列化错误失败"}`
	}
	return string(jsonBytes)
}

// WriteHTTP 将错误写入 HTTP 响应
func (e *CustomError) WriteHTTP(w http.ResponseWriter) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(e.HTTPCode)
	w.Write([]byte(e.ToJSON()))
}

// LogError 记录错误日志(包含原始错误信息)
func (e *CustomError) LogError() {
	if e.Err != nil {
		log.Printf("自定义错误: %s, 详情: %s, 原始错误: %v", e.Message, e.Details, e.Err)
	} else {
		log.Printf("自定义错误: %s, 详情: %s", e.Message, e.Details)
	}
}

// LogErrorWithRequest 记录错误日志(包含请求信息)
func (e *CustomError) LogErrorWithRequest(r *http.Request) {
	if e.Err != nil {
		log.Printf("请求错误 - 方法: %s, 路径: %s, 错误: %s, 详情: %s, 原始错误: %v",
			r.Method, r.URL.Path, e.Message, e.Details, e.Err)
	} else {
		log.Printf("请求错误 - 方法: %s, 路径: %s, 错误: %s, 详情: %s",
			r.Method, r.URL.Path, e.Message, e.Details)
	}
}

// GetOriginalError 获取原始错误(如果存在)
func (e *CustomError) GetOriginalError() error {
	return e.Err
}

// Is 检查错误链中是否包含目标错误
func Is(err, target error) bool {
	if err == target {
		return true
	}

	if customErr, ok := err.(*CustomError); ok && customErr.Err != nil {
		return Is(customErr.Err, target)
	}

	return false
}

如有转载或 CV 的请标注本站原文地址