1072 lines
26 KiB
Go
1072 lines
26 KiB
Go
/*
|
||
02-custom-errors.go - Go 语言自定义错误详解
|
||
|
||
学习目标:
|
||
1. 理解自定义错误的必要性
|
||
2. 掌握自定义错误类型的设计
|
||
3. 学会实现 error 接口
|
||
4. 了解错误的分类和层次
|
||
5. 掌握错误的序列化和反序列化
|
||
|
||
知识点:
|
||
- 自定义错误类型的设计
|
||
- 实现 error 接口
|
||
- 错误的分类和层次结构
|
||
- 错误的附加信息
|
||
- 错误的序列化
|
||
- 错误的本地化
|
||
*/
|
||
|
||
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"net/http"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
func main() {
|
||
fmt.Println("=== Go 语言自定义错误详解 ===\\n")
|
||
|
||
// 演示基本自定义错误
|
||
demonstrateBasicCustomErrors()
|
||
|
||
// 演示结构化错误
|
||
demonstrateStructuredErrors()
|
||
|
||
// 演示错误分类
|
||
demonstrateErrorClassification()
|
||
|
||
// 演示错误层次结构
|
||
demonstrateErrorHierarchy()
|
||
|
||
// 演示错误的附加信息
|
||
demonstrateErrorWithDetails()
|
||
|
||
// 演示错误的序列化
|
||
demonstrateErrorSerialization()
|
||
|
||
// 演示实际应用场景
|
||
demonstratePracticalCustomErrors()
|
||
}
|
||
|
||
// demonstrateBasicCustomErrors 演示基本自定义错误
|
||
func demonstrateBasicCustomErrors() {
|
||
fmt.Println("1. 基本自定义错误:")
|
||
|
||
// 简单自定义错误
|
||
fmt.Printf(" 简单自定义错误:\\n")
|
||
|
||
err := &SimpleError{Message: "这是一个简单的自定义错误"}
|
||
fmt.Printf(" 错误: %v\\n", err)
|
||
fmt.Printf(" 错误类型: %T\\n", err)
|
||
|
||
// 带代码的错误
|
||
fmt.Printf(" 带代码的错误:\\n")
|
||
|
||
codeErr := &CodeError{
|
||
Code: 1001,
|
||
Message: "用户输入无效",
|
||
}
|
||
fmt.Printf(" 错误: %v\\n", codeErr)
|
||
fmt.Printf(" 错误代码: %d\\n", codeErr.Code)
|
||
|
||
// 带时间戳的错误
|
||
fmt.Printf(" 带时间戳的错误:\\n")
|
||
|
||
timeErr := &TimestampError{
|
||
Message: "操作失败",
|
||
Timestamp: time.Now(),
|
||
}
|
||
fmt.Printf(" 错误: %v\\n", timeErr)
|
||
|
||
// 带上下文的错误
|
||
fmt.Printf(" 带上下文的错误:\\n")
|
||
|
||
contextErr := &ContextError{
|
||
Operation: "数据库查询",
|
||
Resource: "用户表",
|
||
Message: "连接超时",
|
||
}
|
||
fmt.Printf(" 错误: %v\\n", contextErr)
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateStructuredErrors 演示结构化错误
|
||
func demonstrateStructuredErrors() {
|
||
fmt.Println("2. 结构化错误:")
|
||
|
||
// 验证错误
|
||
fmt.Printf(" 验证错误:\\n")
|
||
|
||
validationErr := &ValidationError{
|
||
Field: "email",
|
||
Value: "invalid-email",
|
||
Rule: "email_format",
|
||
Message: "邮箱格式不正确",
|
||
}
|
||
fmt.Printf(" 验证错误: %v\\n", validationErr)
|
||
|
||
// HTTP 错误
|
||
fmt.Printf(" HTTP 错误:\\n")
|
||
|
||
httpErr := &HTTPError{
|
||
StatusCode: http.StatusNotFound,
|
||
Method: "GET",
|
||
URL: "/api/users/999",
|
||
Message: "用户不存在",
|
||
}
|
||
fmt.Printf(" HTTP错误: %v\\n", httpErr)
|
||
fmt.Printf(" 状态码: %d\\n", httpErr.StatusCode)
|
||
fmt.Printf(" 是否客户端错误: %t\\n", httpErr.IsClientError())
|
||
fmt.Printf(" 是否服务器错误: %t\\n", httpErr.IsServerError())
|
||
|
||
// 数据库错误
|
||
fmt.Printf(" 数据库错误:\\n")
|
||
|
||
dbErr := &DatabaseError{
|
||
Operation: "INSERT",
|
||
Table: "users",
|
||
Query: "INSERT INTO users (name, email) VALUES (?, ?)",
|
||
SQLState: "23000",
|
||
Message: "重复键值违反唯一约束",
|
||
}
|
||
fmt.Printf(" 数据库错误: %v\\n", dbErr)
|
||
fmt.Printf(" 是否约束违反: %t\\n", dbErr.IsConstraintViolation())
|
||
|
||
// 业务逻辑错误
|
||
fmt.Printf(" 业务逻辑错误:\\n")
|
||
|
||
businessErr := &BusinessError{
|
||
Domain: "用户管理",
|
||
Rule: "年龄限制",
|
||
Message: "用户年龄必须在18-65岁之间",
|
||
Severity: "ERROR",
|
||
Retryable: false,
|
||
}
|
||
fmt.Printf(" 业务错误: %v\\n", businessErr)
|
||
fmt.Printf(" 可重试: %t\\n", businessErr.Retryable)
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateErrorClassification 演示错误分类
|
||
func demonstrateErrorClassification() {
|
||
fmt.Println("3. 错误分类:")
|
||
|
||
// 临时错误 vs 永久错误
|
||
fmt.Printf(" 临时错误 vs 永久错误:\\n")
|
||
|
||
tempErr := &TemporaryError{
|
||
Message: "网络连接超时",
|
||
Temporary: true,
|
||
RetryAfter: 5 * time.Second,
|
||
}
|
||
fmt.Printf(" 临时错误: %v\\n", tempErr)
|
||
fmt.Printf(" 是否临时: %t\\n", tempErr.IsTemporary())
|
||
fmt.Printf(" 重试间隔: %v\\n", tempErr.RetryAfter)
|
||
|
||
permErr := &PermanentError{
|
||
Message: "认证失败:无效的API密钥",
|
||
Reason: "INVALID_CREDENTIALS",
|
||
}
|
||
fmt.Printf(" 永久错误: %v\\n", permErr)
|
||
fmt.Printf(" 是否临时: %t\\n", permErr.IsTemporary())
|
||
|
||
// 可恢复错误 vs 不可恢复错误
|
||
fmt.Printf(" 可恢复错误 vs 不可恢复错误:\\n")
|
||
|
||
recoverableErr := &RecoverableError{
|
||
Message: "磁盘空间不足",
|
||
RecoveryHint: "请清理磁盘空间后重试",
|
||
}
|
||
fmt.Printf(" 可恢复错误: %v\\n", recoverableErr)
|
||
fmt.Printf(" 恢复提示: %s\\n", recoverableErr.RecoveryHint)
|
||
|
||
fatalErr := &FatalError{
|
||
Message: "系统内存不足,程序无法继续运行",
|
||
Cause: "OUT_OF_MEMORY",
|
||
}
|
||
fmt.Printf(" 致命错误: %v\\n", fatalErr)
|
||
|
||
// 用户错误 vs 系统错误
|
||
fmt.Printf(" 用户错误 vs 系统错误:\\n")
|
||
|
||
userErr := &UserError{
|
||
Message: "密码长度至少8位",
|
||
UserMessage: "请输入至少8位字符的密码",
|
||
Field: "password",
|
||
}
|
||
fmt.Printf(" 用户错误: %v\\n", userErr)
|
||
fmt.Printf(" 用户友好消息: %s\\n", userErr.UserMessage)
|
||
|
||
systemErr := &SystemError{
|
||
Message: "数据库连接池耗尽",
|
||
Component: "database",
|
||
Level: "CRITICAL",
|
||
}
|
||
fmt.Printf(" 系统错误: %v\\n", systemErr)
|
||
fmt.Printf(" 组件: %s\\n", systemErr.Component)
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateErrorHierarchy 演示错误层次结构
|
||
func demonstrateErrorHierarchy() {
|
||
fmt.Println("4. 错误层次结构:")
|
||
|
||
// 基础错误接口
|
||
fmt.Printf(" 基础错误接口:\\n")
|
||
|
||
var err error
|
||
|
||
// 网络错误层次
|
||
err = &NetworkError{
|
||
BaseError: BaseError{
|
||
Code: "NET001",
|
||
Message: "网络连接失败",
|
||
Timestamp: time.Now(),
|
||
},
|
||
Host: "api.example.com",
|
||
Port: 443,
|
||
Timeout: 30 * time.Second,
|
||
}
|
||
|
||
fmt.Printf(" 网络错误: %v\\n", err)
|
||
|
||
// 检查错误类型
|
||
if netErr, ok := err.(*NetworkError); ok {
|
||
fmt.Printf(" 主机: %s\\n", netErr.Host)
|
||
fmt.Printf(" 端口: %d\\n", netErr.Port)
|
||
fmt.Printf(" 超时: %v\\n", netErr.Timeout)
|
||
}
|
||
|
||
// 应用错误层次
|
||
err = &ApplicationError{
|
||
BaseError: BaseError{
|
||
Code: "APP001",
|
||
Message: "应用程序错误",
|
||
Timestamp: time.Now(),
|
||
},
|
||
Module: "用户服务",
|
||
Version: "1.2.3",
|
||
Stack: []string{"main.go:42", "user.go:15", "db.go:89"},
|
||
}
|
||
|
||
fmt.Printf(" 应用错误: %v\\n", err)
|
||
|
||
if appErr, ok := err.(*ApplicationError); ok {
|
||
fmt.Printf(" 模块: %s\\n", appErr.Module)
|
||
fmt.Printf(" 版本: %s\\n", appErr.Version)
|
||
fmt.Printf(" 调用栈: %v\\n", appErr.Stack)
|
||
}
|
||
|
||
// 错误链
|
||
fmt.Printf(" 错误链:\\n")
|
||
|
||
rootErr := &BaseError{
|
||
Code: "DB001",
|
||
Message: "数据库连接失败",
|
||
}
|
||
|
||
serviceErr := &ServiceError{
|
||
BaseError: BaseError{
|
||
Code: "SVC001",
|
||
Message: "服务调用失败",
|
||
},
|
||
Service: "用户服务",
|
||
Cause: rootErr,
|
||
}
|
||
|
||
apiErr := &APIError{
|
||
BaseError: BaseError{
|
||
Code: "API001",
|
||
Message: "API请求失败",
|
||
},
|
||
Endpoint: "/api/users",
|
||
Cause: serviceErr,
|
||
}
|
||
|
||
fmt.Printf(" API错误链: %v\\n", apiErr)
|
||
printErrorChain(apiErr, " ")
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateErrorWithDetails 演示错误的附加信息
|
||
func demonstrateErrorWithDetails() {
|
||
fmt.Println("5. 错误的附加信息:")
|
||
|
||
// 带详细信息的错误
|
||
fmt.Printf(" 带详细信息的错误:\\n")
|
||
|
||
detailedErr := &DetailedError{
|
||
Code: "VALIDATION_FAILED",
|
||
Message: "数据验证失败",
|
||
Details: map[string]interface{}{
|
||
"field": "age",
|
||
"value": -5,
|
||
"constraint": "must be positive",
|
||
"suggested_fix": "请输入正数",
|
||
"documentation": "https://docs.example.com/validation",
|
||
},
|
||
}
|
||
|
||
fmt.Printf(" 详细错误: %v\\n", detailedErr)
|
||
fmt.Printf(" 详细信息:\\n")
|
||
for key, value := range detailedErr.Details {
|
||
fmt.Printf(" %s: %v\\n", key, value)
|
||
}
|
||
|
||
// 带元数据的错误
|
||
fmt.Printf(" 带元数据的错误:\\n")
|
||
|
||
metaErr := &MetadataError{
|
||
Message: "文件处理失败",
|
||
Metadata: ErrorMetadata{
|
||
RequestID: "req-12345",
|
||
UserID: "user-67890",
|
||
SessionID: "sess-abcdef",
|
||
TraceID: "trace-xyz789",
|
||
Environment: "production",
|
||
Version: "v1.0.0",
|
||
},
|
||
}
|
||
|
||
fmt.Printf(" 元数据错误: %v\\n", metaErr)
|
||
fmt.Printf(" 请求ID: %s\\n", metaErr.Metadata.RequestID)
|
||
fmt.Printf(" 用户ID: %s\\n", metaErr.Metadata.UserID)
|
||
fmt.Printf(" 环境: %s\\n", metaErr.Metadata.Environment)
|
||
|
||
// 带建议的错误
|
||
fmt.Printf(" 带建议的错误:\\n")
|
||
|
||
suggestErr := &SuggestedError{
|
||
Message: "配置文件格式错误",
|
||
Suggestions: []string{
|
||
"检查JSON语法是否正确",
|
||
"确保所有字符串都用双引号包围",
|
||
"验证配置文件的结构",
|
||
"参考示例配置文件",
|
||
},
|
||
HelpURL: "https://docs.example.com/config",
|
||
}
|
||
|
||
fmt.Printf(" 建议错误: %v\\n", suggestErr)
|
||
fmt.Printf(" 建议:\\n")
|
||
for i, suggestion := range suggestErr.Suggestions {
|
||
fmt.Printf(" %d. %s\\n", i+1, suggestion)
|
||
}
|
||
fmt.Printf(" 帮助链接: %s\\n", suggestErr.HelpURL)
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateErrorSerialization 演示错误的序列化
|
||
func demonstrateErrorSerialization() {
|
||
fmt.Println("6. 错误的序列化:")
|
||
|
||
// JSON 序列化
|
||
fmt.Printf(" JSON 序列化:\\n")
|
||
|
||
serializableErr := &SerializableError{
|
||
Code: "ERR001",
|
||
Message: "操作失败",
|
||
Timestamp: time.Now(),
|
||
Details: map[string]interface{}{
|
||
"operation": "user_create",
|
||
"reason": "duplicate_email",
|
||
"email": "user@example.com",
|
||
},
|
||
}
|
||
|
||
// 序列化为JSON
|
||
jsonData, err := json.MarshalIndent(serializableErr, "", " ")
|
||
if err != nil {
|
||
fmt.Printf(" 序列化失败: %v\\n", err)
|
||
} else {
|
||
fmt.Printf(" JSON序列化结果:\\n%s\\n", string(jsonData))
|
||
}
|
||
|
||
// 从JSON反序列化
|
||
var deserializedErr SerializableError
|
||
err = json.Unmarshal(jsonData, &deserializedErr)
|
||
if err != nil {
|
||
fmt.Printf(" 反序列化失败: %v\\n", err)
|
||
} else {
|
||
fmt.Printf(" 反序列化结果: %v\\n", &deserializedErr)
|
||
}
|
||
|
||
// 错误传输
|
||
fmt.Printf(" 错误传输:\\n")
|
||
|
||
transportErr := &TransportError{
|
||
ID: "err-12345",
|
||
Type: "ValidationError",
|
||
Message: "输入验证失败",
|
||
Code: 400,
|
||
Timestamp: time.Now().Unix(),
|
||
Data: map[string]interface{}{
|
||
"field": "email",
|
||
"value": "invalid",
|
||
},
|
||
}
|
||
|
||
// 模拟网络传输
|
||
transmitted := transmitError(transportErr)
|
||
fmt.Printf(" 传输的错误: %v\\n", transmitted)
|
||
|
||
// 错误本地化
|
||
fmt.Printf(" 错误本地化:\\n")
|
||
|
||
localizedErr := &LocalizedError{
|
||
Code: "USER_NOT_FOUND",
|
||
Message: "User not found",
|
||
Locale: "en",
|
||
Translations: map[string]string{
|
||
"en": "User not found",
|
||
"zh": "用户未找到",
|
||
"ja": "ユーザーが見つかりません",
|
||
"fr": "Utilisateur non trouvé",
|
||
},
|
||
}
|
||
|
||
fmt.Printf(" 英文: %s\\n", localizedErr.GetMessage("en"))
|
||
fmt.Printf(" 中文: %s\\n", localizedErr.GetMessage("zh"))
|
||
fmt.Printf(" 日文: %s\\n", localizedErr.GetMessage("ja"))
|
||
fmt.Printf(" 法文: %s\\n", localizedErr.GetMessage("fr"))
|
||
fmt.Printf(" 默认: %s\\n", localizedErr.GetMessage("unknown"))
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstratePracticalCustomErrors 演示实际应用场景
|
||
func demonstratePracticalCustomErrors() {
|
||
fmt.Println("7. 实际应用场景:")
|
||
|
||
// Web API 错误处理
|
||
fmt.Printf(" Web API 错误处理:\\n")
|
||
|
||
apiErrors := []error{
|
||
&APIError{
|
||
BaseError: BaseError{Code: "AUTH001", Message: "认证失败"},
|
||
Endpoint: "/api/login",
|
||
},
|
||
&APIError{
|
||
BaseError: BaseError{Code: "RATE001", Message: "请求频率超限"},
|
||
Endpoint: "/api/data",
|
||
},
|
||
&APIError{
|
||
BaseError: BaseError{Code: "VAL001", Message: "参数验证失败"},
|
||
Endpoint: "/api/users",
|
||
},
|
||
}
|
||
|
||
for _, err := range apiErrors {
|
||
handleAPIError(err)
|
||
}
|
||
|
||
// 数据库操作错误
|
||
fmt.Printf(" 数据库操作错误:\\n")
|
||
|
||
dbErrors := []error{
|
||
&DatabaseError{
|
||
Operation: "SELECT",
|
||
Table: "users",
|
||
SQLState: "42000",
|
||
Message: "语法错误",
|
||
},
|
||
&DatabaseError{
|
||
Operation: "INSERT",
|
||
Table: "orders",
|
||
SQLState: "23505",
|
||
Message: "唯一约束违反",
|
||
},
|
||
&DatabaseError{
|
||
Operation: "UPDATE",
|
||
Table: "products",
|
||
SQLState: "40001",
|
||
Message: "死锁检测",
|
||
},
|
||
}
|
||
|
||
for _, err := range dbErrors {
|
||
handleDatabaseError(err)
|
||
}
|
||
|
||
// 文件操作错误
|
||
fmt.Printf(" 文件操作错误:\\n")
|
||
|
||
fileErr := &FileError{
|
||
Operation: "read",
|
||
Path: "/etc/config.json",
|
||
Message: "权限被拒绝",
|
||
OSError: "permission denied",
|
||
}
|
||
|
||
handleFileError(fileErr)
|
||
|
||
// 业务规则错误
|
||
fmt.Printf(" 业务规则错误:\\n")
|
||
|
||
businessErrors := []*BusinessRuleError{
|
||
{
|
||
Rule: "最小订单金额",
|
||
Description: "订单金额必须大于100元",
|
||
Value: "50",
|
||
Constraint: "> 100",
|
||
},
|
||
{
|
||
Rule: "库存检查",
|
||
Description: "商品库存不足",
|
||
Value: "2",
|
||
Constraint: ">= 5",
|
||
},
|
||
}
|
||
|
||
for _, err := range businessErrors {
|
||
fmt.Printf(" 业务规则违反: %v\\n", err)
|
||
}
|
||
|
||
// 集成错误处理
|
||
fmt.Printf(" 集成错误处理:\\n")
|
||
|
||
integrationErr := &IntegrationError{
|
||
Service: "支付服务",
|
||
Endpoint: "https://pay.example.com/api/charge",
|
||
Method: "POST",
|
||
StatusCode: 503,
|
||
Message: "服务不可用",
|
||
RetryAfter: 30 * time.Second,
|
||
Recoverable: true,
|
||
}
|
||
|
||
fmt.Printf(" 集成错误: %v\\n", integrationErr)
|
||
fmt.Printf(" 可恢复: %t\\n", integrationErr.Recoverable)
|
||
fmt.Printf(" 重试间隔: %v\\n", integrationErr.RetryAfter)
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// ========== 自定义错误类型定义 ==========
|
||
|
||
// SimpleError 简单自定义错误
|
||
type SimpleError struct {
|
||
Message string
|
||
}
|
||
|
||
func (e *SimpleError) Error() string {
|
||
return e.Message
|
||
}
|
||
|
||
// CodeError 带代码的错误
|
||
type CodeError struct {
|
||
Code int
|
||
Message string
|
||
}
|
||
|
||
func (e *CodeError) Error() string {
|
||
return fmt.Sprintf("[%d] %s", e.Code, e.Message)
|
||
}
|
||
|
||
// TimestampError 带时间戳的错误
|
||
type TimestampError struct {
|
||
Message string
|
||
Timestamp time.Time
|
||
}
|
||
|
||
func (e *TimestampError) Error() string {
|
||
return fmt.Sprintf("%s (发生时间: %s)", e.Message, e.Timestamp.Format("2006-01-02 15:04:05"))
|
||
}
|
||
|
||
// ContextError 带上下文的错误
|
||
type ContextError struct {
|
||
Operation string
|
||
Resource string
|
||
Message string
|
||
}
|
||
|
||
func (e *ContextError) Error() string {
|
||
return fmt.Sprintf("操作 '%s' 在资源 '%s' 上失败: %s", e.Operation, e.Resource, e.Message)
|
||
}
|
||
|
||
// ValidationError 验证错误
|
||
type ValidationError struct {
|
||
Field string
|
||
Value string
|
||
Rule string
|
||
Message string
|
||
}
|
||
|
||
func (e *ValidationError) Error() string {
|
||
return fmt.Sprintf("字段 '%s' 验证失败 (值: '%s', 规则: %s): %s",
|
||
e.Field, e.Value, e.Rule, e.Message)
|
||
}
|
||
|
||
// HTTPError HTTP错误
|
||
type HTTPError struct {
|
||
StatusCode int
|
||
Method string
|
||
URL string
|
||
Message string
|
||
}
|
||
|
||
func (e *HTTPError) Error() string {
|
||
return fmt.Sprintf("HTTP %d: %s %s - %s", e.StatusCode, e.Method, e.URL, e.Message)
|
||
}
|
||
|
||
func (e *HTTPError) IsClientError() bool {
|
||
return e.StatusCode >= 400 && e.StatusCode < 500
|
||
}
|
||
|
||
func (e *HTTPError) IsServerError() bool {
|
||
return e.StatusCode >= 500 && e.StatusCode < 600
|
||
}
|
||
|
||
// DatabaseError 数据库错误
|
||
type DatabaseError struct {
|
||
Operation string
|
||
Table string
|
||
Query string
|
||
SQLState string
|
||
Message string
|
||
}
|
||
|
||
func (e *DatabaseError) Error() string {
|
||
return fmt.Sprintf("数据库错误 [%s]: %s 操作在表 '%s' 上失败 - %s",
|
||
e.SQLState, e.Operation, e.Table, e.Message)
|
||
}
|
||
|
||
func (e *DatabaseError) IsConstraintViolation() bool {
|
||
return strings.HasPrefix(e.SQLState, "23")
|
||
}
|
||
|
||
// BusinessError 业务逻辑错误
|
||
type BusinessError struct {
|
||
Domain string
|
||
Rule string
|
||
Message string
|
||
Severity string
|
||
Retryable bool
|
||
}
|
||
|
||
func (e *BusinessError) Error() string {
|
||
return fmt.Sprintf("业务错误 [%s]: 规则 '%s' 违反 - %s", e.Domain, e.Rule, e.Message)
|
||
}
|
||
|
||
// TemporaryError 临时错误
|
||
type TemporaryError struct {
|
||
Message string
|
||
Temporary bool
|
||
RetryAfter time.Duration
|
||
}
|
||
|
||
func (e *TemporaryError) Error() string {
|
||
return e.Message
|
||
}
|
||
|
||
func (e *TemporaryError) IsTemporary() bool {
|
||
return e.Temporary
|
||
}
|
||
|
||
// PermanentError 永久错误
|
||
type PermanentError struct {
|
||
Message string
|
||
Reason string
|
||
}
|
||
|
||
func (e *PermanentError) Error() string {
|
||
return fmt.Sprintf("%s (原因: %s)", e.Message, e.Reason)
|
||
}
|
||
|
||
func (e *PermanentError) IsTemporary() bool {
|
||
return false
|
||
}
|
||
|
||
// RecoverableError 可恢复错误
|
||
type RecoverableError struct {
|
||
Message string
|
||
RecoveryHint string
|
||
}
|
||
|
||
func (e *RecoverableError) Error() string {
|
||
return e.Message
|
||
}
|
||
|
||
// FatalError 致命错误
|
||
type FatalError struct {
|
||
Message string
|
||
Cause string
|
||
}
|
||
|
||
func (e *FatalError) Error() string {
|
||
return fmt.Sprintf("致命错误: %s (原因: %s)", e.Message, e.Cause)
|
||
}
|
||
|
||
// UserError 用户错误
|
||
type UserError struct {
|
||
Message string
|
||
UserMessage string
|
||
Field string
|
||
}
|
||
|
||
func (e *UserError) Error() string {
|
||
return e.Message
|
||
}
|
||
|
||
// SystemError 系统错误
|
||
type SystemError struct {
|
||
Message string
|
||
Component string
|
||
Level string
|
||
}
|
||
|
||
func (e *SystemError) Error() string {
|
||
return fmt.Sprintf("系统错误 [%s/%s]: %s", e.Component, e.Level, e.Message)
|
||
}
|
||
|
||
// BaseError 基础错误
|
||
type BaseError struct {
|
||
Code string
|
||
Message string
|
||
Timestamp time.Time
|
||
}
|
||
|
||
func (e *BaseError) Error() string {
|
||
return fmt.Sprintf("[%s] %s", e.Code, e.Message)
|
||
}
|
||
|
||
// NetworkError 网络错误
|
||
type NetworkError struct {
|
||
BaseError
|
||
Host string
|
||
Port int
|
||
Timeout time.Duration
|
||
}
|
||
|
||
func (e *NetworkError) Error() string {
|
||
return fmt.Sprintf("网络错误 [%s]: 连接 %s:%d 失败 - %s (超时: %v)",
|
||
e.Code, e.Host, e.Port, e.Message, e.Timeout)
|
||
}
|
||
|
||
// ApplicationError 应用错误
|
||
type ApplicationError struct {
|
||
BaseError
|
||
Module string
|
||
Version string
|
||
Stack []string
|
||
}
|
||
|
||
func (e *ApplicationError) Error() string {
|
||
return fmt.Sprintf("应用错误 [%s]: 模块 '%s' v%s - %s",
|
||
e.Code, e.Module, e.Version, e.Message)
|
||
}
|
||
|
||
// ServiceError 服务错误
|
||
type ServiceError struct {
|
||
BaseError
|
||
Service string
|
||
Cause error
|
||
}
|
||
|
||
func (e *ServiceError) Error() string {
|
||
if e.Cause != nil {
|
||
return fmt.Sprintf("服务错误 [%s]: 服务 '%s' - %s (原因: %v)",
|
||
e.Code, e.Service, e.Message, e.Cause)
|
||
}
|
||
return fmt.Sprintf("服务错误 [%s]: 服务 '%s' - %s", e.Code, e.Service, e.Message)
|
||
}
|
||
|
||
func (e *ServiceError) Unwrap() error {
|
||
return e.Cause
|
||
}
|
||
|
||
// APIError API错误
|
||
type APIError struct {
|
||
BaseError
|
||
Endpoint string
|
||
Cause error
|
||
}
|
||
|
||
func (e *APIError) Error() string {
|
||
if e.Cause != nil {
|
||
return fmt.Sprintf("API错误 [%s]: 端点 '%s' - %s (原因: %v)",
|
||
e.Code, e.Endpoint, e.Message, e.Cause)
|
||
}
|
||
return fmt.Sprintf("API错误 [%s]: 端点 '%s' - %s", e.Code, e.Endpoint, e.Message)
|
||
}
|
||
|
||
func (e *APIError) Unwrap() error {
|
||
return e.Cause
|
||
}
|
||
|
||
// DetailedError 带详细信息的错误
|
||
type DetailedError struct {
|
||
Code string
|
||
Message string
|
||
Details map[string]interface{}
|
||
}
|
||
|
||
func (e *DetailedError) Error() string {
|
||
return fmt.Sprintf("[%s] %s", e.Code, e.Message)
|
||
}
|
||
|
||
// ErrorMetadata 错误元数据
|
||
type ErrorMetadata struct {
|
||
RequestID string `json:"request_id"`
|
||
UserID string `json:"user_id"`
|
||
SessionID string `json:"session_id"`
|
||
TraceID string `json:"trace_id"`
|
||
Environment string `json:"environment"`
|
||
Version string `json:"version"`
|
||
}
|
||
|
||
// MetadataError 带元数据的错误
|
||
type MetadataError struct {
|
||
Message string
|
||
Metadata ErrorMetadata
|
||
}
|
||
|
||
func (e *MetadataError) Error() string {
|
||
return fmt.Sprintf("%s [请求ID: %s]", e.Message, e.Metadata.RequestID)
|
||
}
|
||
|
||
// SuggestedError 带建议的错误
|
||
type SuggestedError struct {
|
||
Message string
|
||
Suggestions []string
|
||
HelpURL string
|
||
}
|
||
|
||
func (e *SuggestedError) Error() string {
|
||
return e.Message
|
||
}
|
||
|
||
// SerializableError 可序列化错误
|
||
type SerializableError struct {
|
||
Code string `json:"code"`
|
||
Message string `json:"message"`
|
||
Timestamp time.Time `json:"timestamp"`
|
||
Details map[string]interface{} `json:"details,omitempty"`
|
||
}
|
||
|
||
func (e *SerializableError) Error() string {
|
||
return fmt.Sprintf("[%s] %s", e.Code, e.Message)
|
||
}
|
||
|
||
// TransportError 传输错误
|
||
type TransportError struct {
|
||
ID string `json:"id"`
|
||
Type string `json:"type"`
|
||
Message string `json:"message"`
|
||
Code int `json:"code"`
|
||
Timestamp int64 `json:"timestamp"`
|
||
Data map[string]interface{} `json:"data,omitempty"`
|
||
}
|
||
|
||
func (e *TransportError) Error() string {
|
||
return fmt.Sprintf("[%s] %s: %s", e.ID, e.Type, e.Message)
|
||
}
|
||
|
||
// LocalizedError 本地化错误
|
||
type LocalizedError struct {
|
||
Code string
|
||
Message string
|
||
Locale string
|
||
Translations map[string]string
|
||
}
|
||
|
||
func (e *LocalizedError) Error() string {
|
||
return e.Message
|
||
}
|
||
|
||
func (e *LocalizedError) GetMessage(locale string) string {
|
||
if msg, ok := e.Translations[locale]; ok {
|
||
return msg
|
||
}
|
||
return e.Message // 返回默认消息
|
||
}
|
||
|
||
// FileError 文件错误
|
||
type FileError struct {
|
||
Operation string
|
||
Path string
|
||
Message string
|
||
OSError string
|
||
}
|
||
|
||
func (e *FileError) Error() string {
|
||
return fmt.Sprintf("文件操作 '%s' 在路径 '%s' 失败: %s (%s)",
|
||
e.Operation, e.Path, e.Message, e.OSError)
|
||
}
|
||
|
||
// BusinessRuleError 业务规则错误
|
||
type BusinessRuleError struct {
|
||
Rule string
|
||
Description string
|
||
Value string
|
||
Constraint string
|
||
}
|
||
|
||
func (e *BusinessRuleError) Error() string {
|
||
return fmt.Sprintf("业务规则 '%s' 违反: %s (值: %s, 约束: %s)",
|
||
e.Rule, e.Description, e.Value, e.Constraint)
|
||
}
|
||
|
||
// IntegrationError 集成错误
|
||
type IntegrationError struct {
|
||
Service string
|
||
Endpoint string
|
||
Method string
|
||
StatusCode int
|
||
Message string
|
||
RetryAfter time.Duration
|
||
Recoverable bool
|
||
}
|
||
|
||
func (e *IntegrationError) Error() string {
|
||
return fmt.Sprintf("集成错误: %s %s %s 返回 %d - %s",
|
||
e.Service, e.Method, e.Endpoint, e.StatusCode, e.Message)
|
||
}
|
||
|
||
// ========== 辅助函数 ==========
|
||
|
||
// printErrorChain 打印错误链
|
||
func printErrorChain(err error, indent string) {
|
||
if err == nil {
|
||
return
|
||
}
|
||
|
||
fmt.Printf("%s- %v\\n", indent, err)
|
||
|
||
// 检查是否有 Unwrap 方法
|
||
if unwrapper, ok := err.(interface{ Unwrap() error }); ok {
|
||
if cause := unwrapper.Unwrap(); cause != nil {
|
||
printErrorChain(cause, indent+" ")
|
||
}
|
||
}
|
||
}
|
||
|
||
// transmitError 模拟错误传输
|
||
func transmitError(err *TransportError) *TransportError {
|
||
// 模拟网络传输过程
|
||
return &TransportError{
|
||
ID: err.ID,
|
||
Type: err.Type,
|
||
Message: err.Message,
|
||
Code: err.Code,
|
||
Timestamp: err.Timestamp,
|
||
Data: err.Data,
|
||
}
|
||
}
|
||
|
||
// handleAPIError 处理API错误
|
||
func handleAPIError(err error) {
|
||
if apiErr, ok := err.(*APIError); ok {
|
||
switch apiErr.Code {
|
||
case "AUTH001":
|
||
fmt.Printf(" 认证错误,需要重新登录\\n")
|
||
case "RATE001":
|
||
fmt.Printf(" 请求频率超限,需要等待\\n")
|
||
case "VAL001":
|
||
fmt.Printf(" 参数验证失败,检查输入\\n")
|
||
default:
|
||
fmt.Printf(" 未知API错误: %v\\n", apiErr)
|
||
}
|
||
}
|
||
}
|
||
|
||
// handleDatabaseError 处理数据库错误
|
||
func handleDatabaseError(err error) {
|
||
if dbErr, ok := err.(*DatabaseError); ok {
|
||
switch {
|
||
case strings.HasPrefix(dbErr.SQLState, "42"):
|
||
fmt.Printf(" SQL语法错误: %v\\n", dbErr)
|
||
case strings.HasPrefix(dbErr.SQLState, "23"):
|
||
fmt.Printf(" 约束违反错误: %v\\n", dbErr)
|
||
case strings.HasPrefix(dbErr.SQLState, "40"):
|
||
fmt.Printf(" 事务错误: %v\\n", dbErr)
|
||
default:
|
||
fmt.Printf(" 其他数据库错误: %v\\n", dbErr)
|
||
}
|
||
}
|
||
}
|
||
|
||
// handleFileError 处理文件错误
|
||
func handleFileError(err *FileError) {
|
||
fmt.Printf(" 文件错误: %v\\n", err)
|
||
|
||
switch err.Operation {
|
||
case "read":
|
||
fmt.Printf(" 建议: 检查文件是否存在和读取权限\\n")
|
||
case "write":
|
||
fmt.Printf(" 建议: 检查目录写入权限和磁盘空间\\n")
|
||
case "delete":
|
||
fmt.Printf(" 建议: 检查文件是否被其他程序占用\\n")
|
||
}
|
||
}
|
||
|
||
/*
|
||
运行这个程序:
|
||
go run 02-custom-errors.go
|
||
|
||
学习要点:
|
||
1. 自定义错误类型通过实现 error 接口来创建
|
||
2. 自定义错误可以包含更多的上下文信息
|
||
3. 错误可以按照不同的维度进行分类
|
||
4. 错误层次结构有助于组织和处理复杂的错误情况
|
||
5. 错误的序列化和本地化对于分布式系统很重要
|
||
|
||
自定义错误的优势:
|
||
1. 提供更多的上下文信息
|
||
2. 支持错误分类和层次结构
|
||
3. 便于错误处理和恢复
|
||
4. 支持错误的序列化和传输
|
||
5. 提供更好的用户体验
|
||
|
||
错误设计原则:
|
||
1. 错误信息应该清晰、准确
|
||
2. 包含足够的上下文信息
|
||
3. 支持错误的分类和处理
|
||
4. 考虑错误的传播和包装
|
||
5. 提供恢复建议和帮助信息
|
||
|
||
错误分类维度:
|
||
1. 临时性:临时错误 vs 永久错误
|
||
2. 可恢复性:可恢复错误 vs 不可恢复错误
|
||
3. 责任方:用户错误 vs 系统错误
|
||
4. 严重程度:警告 vs 错误 vs 致命错误
|
||
5. 来源:网络错误 vs 数据库错误 vs 业务错误
|
||
|
||
错误层次结构:
|
||
1. 基础错误:定义通用的错误属性
|
||
2. 分类错误:按照错误类型分类
|
||
3. 具体错误:特定场景的错误实现
|
||
4. 错误链:支持错误的嵌套和包装
|
||
5. 错误接口:定义错误的行为
|
||
|
||
实际应用考虑:
|
||
1. 错误码的设计和管理
|
||
2. 错误信息的国际化
|
||
3. 错误的日志记录
|
||
4. 错误的监控和告警
|
||
5. 错误的用户友好展示
|
||
|
||
最佳实践:
|
||
1. 为不同的错误场景定义专门的错误类型
|
||
2. 在错误中包含足够的上下文信息
|
||
3. 支持错误的包装和解包
|
||
4. 提供错误的分类和判断方法
|
||
5. 考虑错误的序列化和传输需求
|
||
|
||
注意事项:
|
||
1. 避免错误信息中包含敏感信息
|
||
2. 错误类型不要过于复杂
|
||
3. 保持错误接口的一致性
|
||
4. 考虑错误的性能影响
|
||
5. 提供适当的错误恢复机制
|
||
*/
|