错误处理
VEF Framework 中的错误处理最佳实践。
API 响应
成功响应
go
// Return data
return api.Success(ctx, user)
// Return with message
return api.SuccessWithMessage(ctx, "User created successfully", user)
// Return empty success
return api.Success(ctx, nil)错误响应
go
// General error
return api.Error(ctx, "Something went wrong")
// Not found
return api.NotFound(ctx, "User not found")
// Forbidden
return api.Forbidden(ctx, "Permission denied")
// Validation error
return api.ValidationError(ctx, validationErrors)
// Custom status code
return api.ErrorWithCode(ctx, 400, "Invalid request")错误类型
定义业务错误
go
package errors
import "errors"
var (
ErrUserNotFound = errors.New("user not found")
ErrInvalidPassword = errors.New("invalid password")
ErrEmailExists = errors.New("email already exists")
ErrInsufficientBalance = errors.New("insufficient balance")
)使用业务错误
go
func (s *UserService) GetUser(id string) (*models.User, error) {
var user models.User
err := orm.DB().First(&user, "id = ?", id).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrUserNotFound
}
return &user, err
}错误包装
添加上下文
go
import "fmt"
func (s *OrderService) CreateOrder(params OrderParams) (*models.Order, error) {
user, err := s.userService.GetUser(params.UserId)
if err != nil {
return nil, fmt.Errorf("failed to get user: %w", err)
}
// ...
}检查错误链
go
if errors.Is(err, ErrUserNotFound) {
return api.NotFound(ctx, "User not found")
}在 API 中处理错误
go
func (r *UserResource) CreateUser(ctx fiber.Ctx, params UserParams) error {
user, err := r.userService.Create(params)
if err != nil {
// Handle specific errors
if errors.Is(err, ErrEmailExists) {
return api.ErrorWithCode(ctx, 409, "Email already exists")
}
if errors.Is(err, ErrInvalidPassword) {
return api.ValidationError(ctx, map[string]string{
"password": "Password does not meet requirements",
})
}
// Log unexpected errors
log.WithError(err).Error("Failed to create user")
return api.Error(ctx, "Failed to create user")
}
return api.Success(ctx, user)
}验证错误
使用验证标签
go
type UserParams struct {
Username string `json:"username" validate:"required,min=3,max=50"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,min=8"`
Age int `json:"age" validate:"omitempty,gte=0,lte=150"`
}自定义验证
go
func validateUserParams(params *UserParams) error {
// Custom validation logic
if strings.Contains(params.Username, " ") {
return errors.New("username cannot contain spaces")
}
// Check password strength
if !isStrongPassword(params.Password) {
return errors.New("password is too weak")
}
return nil
}事务错误处理
go
func (s *OrderService) CreateOrder(params OrderParams) (*models.Order, error) {
var order *models.Order
err := orm.Transaction(func(tx *gorm.DB) error {
// Create order
order = &models.Order{
UserId: params.UserId,
Amount: params.Amount,
}
if err := tx.Create(order).Error; err != nil {
return fmt.Errorf("failed to create order: %w", err)
}
// Deduct balance
result := tx.Model(&models.User{}).
Where("id = ? AND balance >= ?", params.UserId, params.Amount).
Update("balance", gorm.Expr("balance - ?", params.Amount))
if result.RowsAffected == 0 {
return ErrInsufficientBalance
}
return nil
})
if err != nil {
return nil, err
}
return order, nil
}全局错误处理
错误恢复中间件
go
func ErrorRecoveryMiddleware() fiber.Handler {
return func(ctx *fiber.Ctx) error {
defer func() {
if r := recover(); r != nil {
log.WithFields(log.Fields{
"panic": r,
"stack": string(debug.Stack()),
"path": ctx.Path(),
"requestId": contextx.GetRequestId(ctx),
}).Error("Panic recovered")
ctx.Status(500).JSON(fiber.Map{
"success": false,
"message": "Internal server error",
})
}
}()
return ctx.Next()
}
}