Web Go 错误处理示例

Web Go 错误处理示例,包括异常捕获、日志记录和参数验证

💻 日志记录 go

🟢 simple ⭐⭐⭐

使用不同级别和格式将日志写入文件

⏱️ 20 min 🏷️ go, web, error handling
Prerequisites: Basic Go, log package, os package
// Web Go Logging Examples
// Logging to files with different levels and formats

package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// 1. Basic Logging

// BasicLogger wraps standard log package
type BasicLogger struct {
	logger *log.Logger
}

func NewBasicLogger(output io.Writer) *BasicLogger {
	return &BasicLogger{
		logger: log.New(output, "", log.LstdFlags),
	}
}

func (l *BasicLogger) Info(msg string) {
	l.logger.Println("[INFO]", msg)
}

func (l *BasicLogger) Error(msg string) {
	l.logger.Println("[ERROR]", msg)
}

func (l *BasicLogger) Warning(msg string) {
	l.logger.Println("[WARNING]", msg)
}

func (l *BasicLogger) Debug(msg string) {
	l.logger.Println("[DEBUG]", msg)
}

// 2. File Logging

// FileLogger writes logs to a file
type FileLogger struct {
	file   *os.File
	logger *log.Logger
}

func NewFileLogger(filePath string) (*FileLogger, error) {
	// Ensure directory exists
	dir := filepath.Dir(filePath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, fmt.Errorf("error creating directory: %v", err)
	}

	// Open/create log file
	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		return nil, fmt.Errorf("error opening log file: %v", err)
	}

	return &FileLogger{
		file:   file,
		logger: log.New(file, "", log.LstdFlags),
	}, nil
}

func (l *FileLogger) Info(msg string) {
	l.logger.Println("[INFO]", msg)
}

func (l *FileLogger) Error(msg string) {
	l.logger.Println("[ERROR]", msg)
}

func (l *FileLogger) Warning(msg string) {
	l.logger.Println("[WARNING]", msg)
}

func (l *FileLogger) Debug(msg string) {
	l.logger.Println("[DEBUG]", msg)
}

func (l *FileLogger) Close() error {
	return l.file.Close()
}

// 3. Level Logging

type LogLevel int

const (
	DEBUG LogLevel = iota
	INFO
	WARNING
	ERROR
)

// LevelLogger supports different log levels
type LevelLogger struct {
	file       *os.File
	logger     *log.Logger
	minLevel   LogLevel
	levelNames map[LogLevel]string
}

func NewLevelLogger(filePath string, minLevel LogLevel) (*LevelLogger, error) {
	// Ensure directory exists
	dir := filepath.Dir(filePath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, fmt.Errorf("error creating directory: %v", err)
	}

	// Open/create log file
	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		return nil, fmt.Errorf("error opening log file: %v", err)
	}

	levelNames := map[LogLevel]string{
		DEBUG:   "DEBUG",
		INFO:    "INFO",
		WARNING: "WARNING",
		ERROR:   "ERROR",
	}

	return &LevelLogger{
		file:     file,
		logger:   log.New(file, "", log.LstdFlags),
		minLevel: minLevel,
		levelNames: levelNames,
	}, nil
}

func (l *LevelLogger) Log(level LogLevel, msg string) {
	if level < l.minLevel {
		return
	}

	l.logger.Println(fmt.Sprintf("[%s]", l.levelNames[level]), msg)
}

func (l *LevelLogger) Debug(msg string) {
	l.Log(DEBUG, msg)
}

func (l *LevelLogger) Info(msg string) {
	l.Log(INFO, msg)
}

func (l *LevelLogger) Warning(msg string) {
	l.Log(WARNING, msg)
}

func (l *LevelLogger) Error(msg string) {
	l.Log(ERROR, msg)
}

func (l *LevelLogger) Close() error {
	return l.file.Close()
}

// 4. Structured Logging

// LogEntry represents a structured log entry
type LogEntry struct {
	Timestamp time.Time
	Level     string
	Message   string
	Fields    map[string]interface{}
}

// StructuredLogger writes structured logs
type StructuredLogger struct {
	file   *os.File
	encoder func(LogEntry) string
}

func NewStructuredLogger(filePath string) (*StructuredLogger, error) {
	dir := filepath.Dir(filePath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, err
	}

	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		return nil, err
	}

	return &StructuredLogger{
		file:   file,
		encoder: encodeJSON,
	}, nil
}

func encodeJSON(entry LogEntry) string {
	// Simple JSON encoding
	fields := ""
	for k, v := range entry.Fields {
		fields += fmt.Sprintf(", %q: %v", k, v)
	}
	return fmt.Sprintf(
		"{%q: %q, %q: %q, %q: %q%s}\n",
		"timestamp", entry.Timestamp.Format(time.RFC3339),
		"level", entry.Level,
		"message", entry.Message,
		fields,
	)
}

func (l *StructuredLogger) Log(level, message string, fields map[string]interface{}) {
	entry := LogEntry{
		Timestamp: time.Now(),
		Level:     level,
		Message:   message,
		Fields:    fields,
	}

	l.file.WriteString(l.encoder(entry))
}

func (l *StructuredLogger) Close() error {
	return l.file.Close()
}

// 5. Rotating File Logger

// RotatingLogger rotates log files when they reach a size limit
type RotatingLogger struct {
	directory   string
	baseName    string
	maxSize     int64
	currentFile *os.File
	currentSize int64
	logger      *log.Logger
}

func NewRotatingLogger(directory, baseName string, maxSizeMB int) (*RotatingLogger, error) {
	if err := os.MkdirAll(directory, 0755); err != nil {
		return nil, err
	}

	rl := &RotatingLogger{
		directory: directory,
		baseName:  baseName,
		maxSize:   int64(maxSizeMB) * 1024 * 1024,
	}

	if err := rl.openNewFile(); err != nil {
		return nil, err
	}

	return rl, nil
}

func (rl *RotatingLogger) openNewFile() error {
	timestamp := time.Now().Format("20060102_150405")
	filePath := filepath.Join(rl.directory, fmt.Sprintf("%s_%s.log", rl.baseName, timestamp))

	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		return err
	}

	rl.currentFile = file
	rl.currentSize = 0
	rl.logger = log.New(file, "", log.LstdFlags)

	return nil
}

func (rl *RotatingLogger) checkRotation() error {
	if rl.currentSize >= rl.maxSize {
		rl.currentFile.Close()
		return rl.openNewFile()
	}
	return nil
}

func (rl *RotatingLogger) Log(msg string) error {
	if err := rl.checkRotation(); err != nil {
		return err
	}

	rl.logger.Println(msg)
	rl.currentSize += int64(len(msg)) + 1
	return nil
}

func (rl *RotatingLogger) Close() error {
	return rl.currentFile.Close()
}

// 6. Multiple Log Files

// MultiLogger writes to multiple log files
type MultiLogger struct {
	loggers map[string]*log.Logger
	files   map[string]*os.File
}

func NewMultiLogger() *MultiLogger {
	return &MultiLogger{
		loggers: make(map[string]*log.Logger),
		files:   make(map[string]*os.File),
	}
}

func (ml *MultiLogger) AddLogger(name, filePath string) error {
	dir := filepath.Dir(filePath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}

	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		return err
	}

	ml.files[name] = file
	ml.loggers[name] = log.New(file, "", log.LstdFlags)

	return nil
}

func (ml *MultiLogger) Log(loggerName, msg string) {
	if logger, ok := ml.loggers[loggerName]; ok {
		logger.Println(msg)
	}
}

func (ml *MultiLogger) Close() error {
	var lastErr error
	for _, file := range ml.files {
		if err := file.Close(); err != nil {
			lastErr = err
		}
	}
	return lastErr
}

// 7. Context Logger

// ContextLogger adds context to log messages
type ContextLogger struct {
	logger    *log.Logger
	context   string
}

func NewContextLogger(output io.Writer, context string) *ContextLogger {
	return &ContextLogger{
		logger:  log.New(output, "", log.LstdFlags),
		context: context,
	}
}

func (cl *ContextLogger) Log(msg string) {
	cl.logger.Println(fmt.Sprintf("[%s] %s", cl.context, msg))
}

func (cl *ContextLogger) Info(msg string) {
	cl.logger.Println(fmt.Sprintf("[INFO] [%s] %s", cl.context, msg))
}

// 8. Performance Logger

// PerformanceLogger logs execution time
type PerformanceLogger struct {
	logger *log.Logger
	start  time.Time
}

func NewPerformanceLogger(output io.Writer) *PerformanceLogger {
	return &PerformanceLogger{
		logger: log.New(output, "", log.LstdFlags),
		start:  time.Now(),
	}
}

func (pl *PerformanceLogger) LogOperation(operation string) {
	elapsed := time.Since(pl.start)
	pl.logger.Printf("[PERF] %s took %v", operation, elapsed)
	pl.start = time.Now()
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go Logging Examples ===\n")

	// 1. Basic logger
	fmt.Println("--- 1. Basic Logger ---")
	basic := NewBasicLogger(os.Stdout)
	basic.Info("Application started")
	basic.Error("An error occurred")

	// 2. File logger
	fmt.Println("\n--- 2. File Logger ---")
	fileLogger, err := NewFileLogger("logs/app.log")
	if err == nil {
		fileLogger.Info("Logged to file")
		fileLogger.Error("Error logged to file")
		fileLogger.Close()
		fmt.Println("Logs written to file")
	}

	// 3. Level logger
	fmt.Println("\n--- 3. Level Logger ---")
	levelLogger, _ := NewLevelLogger("logs/level.log", INFO)
	levelLogger.Debug("This won't be logged")
	levelLogger.Info("This will be logged")
	levelLogger.Error("Errors are always logged")
	levelLogger.Close()

	// 4. Structured logger
	fmt.Println("\n--- 4. Structured Logger ---")
	structLogger, _ := NewStructuredLogger("logs/structured.log")
	structLogger.Log("INFO", "User logged in", map[string]interface{}{
		"user_id": 123,
		"ip":      "192.168.1.1",
	})
	structLogger.Close()

	// 5. Rotating logger
	fmt.Println("\n--- 5. Rotating Logger ---")
	rotatingLogger, _ := NewRotatingLogger("logs", "app", 1) // 1MB
	for i := 0; i < 100; i++ {
		rotatingLogger.Log(fmt.Sprintf("Log entry %d", i))
	}
	rotatingLogger.Close()

	// 6. Multi logger
	fmt.Println("\n--- 6. Multi Logger ---")
	multi := NewMultiLogger()
	multi.AddLogger("app", "logs/app.log")
	multi.AddLogger("error", "logs/error.log")
	multi.Log("app", "Application log")
	multi.Log("error", "Error log")
	multi.Close()

	// 7. Context logger
	fmt.Println("\n--- 7. Context Logger ---")
	ctx := NewContextLogger(os.Stdout, "AuthService")
	ctx.Info("User authenticated")

	// 8. Performance logger
	fmt.Println("\n--- 8. Performance Logger ---")
	perf := NewPerformanceLogger(os.Stdout)
	time.Sleep(100 * time.Millisecond)
	perf.LogOperation("Database query")
	time.Sleep(50 * time.Millisecond)
	perf.LogOperation("API call")

	fmt.Println("\n=== All Logging Examples Completed ===")
}

💻 参数验证 go

🟢 simple ⭐⭐⭐

使用自定义验证器和错误消息验证函数参数

⏱️ 25 min 🏷️ go, web, error handling
Prerequisites: Basic Go, reflect package
// Web Go Parameter Validation Examples
// Validate function parameters with custom validators

package main

import (
	"errors"
	"fmt"
	"reflect"
	"regexp"
	"strings"
	"unicode"
)

// 1. Basic Validation

// ValidateRequired checks if value is not empty
func ValidateRequired(value interface{}) error {
	if value == nil {
		return errors.New("value is required")
	}

	v := reflect.ValueOf(value)
	switch v.Kind() {
	case reflect.String:
		if strings.TrimSpace(v.String()) == "" {
			return errors.New("value cannot be empty")
		}
	case reflect.Slice, reflect.Map, reflect.Array:
		if v.Len() == 0 {
			return errors.New("value cannot be empty")
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if v.Int() == 0 {
			return errors.New("value cannot be zero")
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		if v.Uint() == 0 {
			return errors.New("value cannot be zero")
		}
	case reflect.Float32, reflect.Float64:
		if v.Float() == 0 {
			return errors.New("value cannot be zero")
		}
	}

	return nil
}

// ValidateMin checks if numeric value is >= min
func ValidateMin(value interface{}, min int) error {
	v := reflect.ValueOf(value)
	switch v.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if v.Int() < int64(min) {
			return fmt.Errorf("value must be at least %d", min)
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		if v.Uint() < uint64(min) {
			return fmt.Errorf("value must be at least %d", min)
		}
	case reflect.Float32, reflect.Float64:
		if v.Float() < float64(min) {
			return fmt.Errorf("value must be at least %d", min)
		}
	default:
		return errors.New("value must be numeric")
	}

	return nil
}

// ValidateMax checks if numeric value is <= max
func ValidateMax(value interface{}, max int) error {
	v := reflect.ValueOf(value)
	switch v.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if v.Int() > int64(max) {
			return fmt.Errorf("value must be at most %d", max)
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		if v.Uint() > uint64(max) {
			return fmt.Errorf("value must be at most %d", max)
		}
	case reflect.Float32, reflect.Float64:
		if v.Float() > float64(max) {
			return fmt.Errorf("value must be at most %d", max)
		}
	default:
		return errors.New("value must be numeric")
	}

	return nil
}

// ValidateLength checks string/slice length
func ValidateLength(value interface{}, min, max int) error {
	v := reflect.ValueOf(value)
	var length int

	switch v.Kind() {
	case reflect.String:
		length = len(strings.TrimSpace(v.String()))
	case reflect.Slice, reflect.Map, reflect.Array:
		length = v.Len()
	default:
		return errors.New("value must be string, slice, or array")
	}

	if length < min {
		return fmt.Errorf("length must be at least %d", min)
	}

	if max > 0 && length > max {
		return fmt.Errorf("length must be at most %d", max)
	}

	return nil
}

// 2. String Validation

// ValidateEmail validates email format
func ValidateEmail(email string) error {
	email = strings.TrimSpace(email)
	if email == "" {
		return errors.New("email is required")
	}

	// Basic email regex
	emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$`)
	if !emailRegex.MatchString(email) {
		return errors.New("invalid email format")
	}

	return nil
}

// ValidateURL validates URL format
func ValidateURL(url string) error {
	url = strings.TrimSpace(url)
	if url == "" {
		return errors.New("URL is required")
	}

	if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
		return errors.New("URL must start with http:// or https://")
	}

	return nil
}

// ValidatePhone validates phone number format
func ValidatePhone(phone string) error {
	phone = strings.TrimSpace(phone)
	if phone == "" {
		return errors.New("phone number is required")
	}

	// Remove common separators
	phone = strings.ReplaceAll(phone, " ", "")
	phone = strings.ReplaceAll(phone, "-", "")
	phone = strings.ReplaceAll(phone, "(", "")
	phone = strings.ReplaceAll(phone, ")", "")

	// Check if digits only
	phoneRegex := regexp.MustCompile(`^\\d{10,15}$`)
	if !phoneRegex.MatchString(phone) {
		return errors.New("invalid phone number format")
	}

	return nil
}

// ValidatePassword validates password strength
func ValidatePassword(password string) error {
	if len(password) < 8 {
		return errors.New("password must be at least 8 characters")
	}

	var (
		hasUpper   bool
		hasLower   bool
		hasNumber  bool
		hasSpecial bool
	)

	for _, char := range password {
		switch {
		case unicode.IsUpper(char):
			hasUpper = true
		case unicode.IsLower(char):
			hasLower = true
		case unicode.IsNumber(char):
			hasNumber = true
		case unicode.IsPunct(char) || unicode.IsSymbol(char):
			hasSpecial = true
		}
	}

	if !hasUpper {
		return errors.New("password must contain uppercase letter")
	}

	if !hasLower {
		return errors.New("password must contain lowercase letter")
	}

	if !hasNumber {
		return errors.New("password must contain number")
	}

	if !hasSpecial {
		return errors.New("password must contain special character")
	}

	return nil
}

// 3. Numeric Validation

// ValidateRange checks if value is in range [min, max]
func ValidateRange(value, min, max int) error {
	if value < min || value > max {
		return fmt.Errorf("value must be between %d and %d", min, max)
	}
	return nil
}

// ValidatePositive checks if value is positive
func ValidatePositive(value int) error {
	if value <= 0 {
		return errors.New("value must be positive")
	}
	return nil
}

// ValidateNegative checks if value is negative
func ValidateNegative(value int) error {
	if value >= 0 {
		return errors.New("value must be negative")
	}
	return nil
}

// ValidatePositiveFloat checks if float value is positive
func ValidatePositiveFloat(value float64) error {
	if value <= 0 {
		return errors.New("value must be positive")
	}
	return nil
}

// 4. Date/Time Validation

// ValidateAge validates age range
func ValidateAge(age int) error {
	return ValidateRange(age, 0, 150)
}

// ValidateYear validates year
func ValidateYear(year int) error {
	const minYear = 1900
	const maxYear = 2100
	return ValidateRange(year, minYear, maxYear)
}

// 5. Custom Validators

// Validator function type
type Validator func(interface{}) error

// ValidateWith runs custom validator
func ValidateWith(value interface{}, validator Validator) error {
	return validator(value)
}

// ValidateStruct validates struct fields
func ValidateStruct(obj interface{}) error {
	v := reflect.ValueOf(obj)
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	if v.Kind() != reflect.Struct {
		return errors.New("expected struct")
	}

	t := v.Type()
	var errs []string

	for i := 0; i < v.NumField(); i++ {
		field := t.Field(i)
		fieldValue := v.Field(i)

		// Check for "validate" tag
		tag := field.Tag.Get("validate")
		if tag != "" {
			// Parse validation rules
			rules := strings.Split(tag, ",")

			for _, rule := range rules {
				if err := applyValidationRule(fieldValue, rule); err != nil {
					errs = append(errs, fmt.Sprintf("%s: %v", field.Name, err))
				}
			}
		}
	}

	if len(errs) > 0 {
		return fmt.Errorf("validation errors: %s", strings.Join(errs, "; "))
	}

	return nil
}

func applyValidationRule(value reflect.Value, rule string) error {
	parts := strings.Split(rule, "=")
	name := parts[0]

	switch name {
	case "required":
		return ValidateRequired(value.Interface())
	case "min":
		if len(parts) == 2 {
			min := parseInt(parts[1])
			return ValidateMin(value.Interface(), min)
		}
	case "max":
		if len(parts) == 2 {
			max := parseInt(parts[1])
			return ValidateMax(value.Interface(), max)
		}
	}

	return nil
}

func parseInt(s string) int {
	var i int
	fmt.Sscanf(s, "%d", &i)
	return i
}

// 6. Validation Result

// ValidationResult holds validation results
type ValidationResult struct {
	Valid  bool
	Errors map[string]string
}

// NewValidationResult creates new validation result
func NewValidationResult() *ValidationResult {
	return &ValidationResult{
		Valid:  true,
		Errors: make(map[string]string),
	}
}

// AddError adds error to validation result
func (vr *ValidationResult) AddError(field, message string) {
	vr.Valid = false
	vr.Errors[field] = message
}

// IsValid returns true if no errors
func (vr *ValidationResult) IsValid() bool {
	return vr.Valid
}

// GetErrors returns all errors
func (vr *ValidationResult) GetErrors() map[string]string {
	return vr.Errors
}

// Error returns error message
func (vr *ValidationResult) Error() string {
	if vr.Valid {
		return ""
	}

	var errs []string
	for field, msg := range vr.Errors {
		errs = append(errs, fmt.Sprintf("%s: %s", field, msg))
	}

	return strings.Join(errs, "; ")
}

// 7. Validation Builder

// ValidationBuilder builds validation rules
type ValidationBuilder struct {
	result *ValidationResult
	value  interface{}
}

func NewValidationBuilder(value interface{}) *ValidationBuilder {
	return &ValidationBuilder{
		result: NewValidationResult(),
		value:  value,
	}
}

func (vb *ValidationBuilder) Required() *ValidationBuilder {
	if err := ValidateRequired(vb.value); err != nil {
		vb.result.AddError("required", err.Error())
	}
	return vb
}

func (vb *ValidationBuilder) Min(min int) *ValidationBuilder {
	if err := ValidateMin(vb.value, min); err != nil {
		vb.result.AddError("min", err.Error())
	}
	return vb
}

func (vb *ValidationBuilder) Max(max int) *ValidationBuilder {
	if err := ValidateMax(vb.value, max); err != nil {
		vb.result.AddError("max", err.Error())
	}
	return vb
}

func (vb *ValidationBuilder) Validate() *ValidationResult {
	return vb.result
}

// 8. Example Usage

type User struct {
	Name     string `validate:"required"`
	Age      int    `validate:"required,min=18,max=120"`
	Email    string
	Password string
}

func ValidateUser(user User) *ValidationResult {
	result := NewValidationResult()

	// Validate name
	if user.Name == "" {
		result.AddError("name", "name is required")
	}

	if len(user.Name) < 2 {
		result.AddError("name", "name must be at least 2 characters")
	}

	// Validate age
	if user.Age < 18 {
		result.AddError("age", "must be at least 18")
	}

	if user.Age > 120 {
		result.AddError("age", "must be at most 120")
	}

	// Validate email
	if err := ValidateEmail(user.Email); err != nil {
		result.AddError("email", err.Error())
	}

	// Validate password
	if err := ValidatePassword(user.Password); err != nil {
		result.AddError("password", err.Error())
	}

	return result
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go Parameter Validation Examples ===\n")

	// 1. Required validation
	fmt.Println("--- 1. Required Validation ---")
	err := ValidateRequired("")
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	}

	err = ValidateRequired("value")
	fmt.Printf("Valid: %v\n", err == nil)

	// 2. Range validation
	fmt.Println("\n--- 2. Range Validation ---")
	err = ValidateMin(5, 10)
	fmt.Printf("Min error: %v\n", err)

	err = ValidateRange(15, 10, 20)
	fmt.Printf("Range valid: %v\n", err == nil)

	// 3. String validation
	fmt.Println("\n--- 3. String Validation ---")
	err = ValidateEmail("invalid-email")
	fmt.Printf("Email error: %v\n", err)

	err = ValidateEmail("[email protected]")
	fmt.Printf("Email valid: %v\n", err == nil)

	// 4. Password validation
	fmt.Println("\n--- 4. Password Validation ---")
	err = ValidatePassword("weak")
	fmt.Printf("Password error: %v\n", err)

	err = ValidatePassword("StrongPass123!")
	fmt.Printf("Password valid: %v\n", err == nil)

	// 5. User validation
	fmt.Println("\n--- 5. User Validation ---")
	user := User{
		Name:     "John",
		Age:      25,
		Email:    "[email protected]",
		Password: "Password123!",
	}

	result := ValidateUser(user)
	if result.IsValid() {
		fmt.Println("User is valid")
	} else {
		fmt.Printf("User validation errors: %v\n", result.Error())
	}

	// 6. Validation builder
	fmt.Println("\n--- 6. Validation Builder ---")
	builderResult := NewValidationBuilder(15).
		Required().
		Min(10).
		Max(20).
		Validate()

	if builderResult.IsValid() {
		fmt.Println("Builder validation passed")
	} else {
		fmt.Printf("Builder errors: %v\n", builderResult.Error())
	}

	fmt.Println("\n=== All Parameter Validation Examples Completed ===")
}

💻 Panic和Recover go

🟡 intermediate ⭐⭐⭐

Go中的panic/recover异常处理机制

⏱️ 25 min 🏷️ go, web, error handling
Prerequisites: Intermediate Go, runtime package
// Web Go Panic/Recover Examples
// Exception handling with panic and recover in Go

package main

import (
	"errors"
	"fmt"
	"runtime"
	"strings"
)

// 1. Basic Panic and Recover

// BasicPanicExample demonstrates basic panic
func BasicPanicExample() {
	fmt.Println("Before panic")
	panic("Something went wrong!")
	fmt.Println("This will not be printed") // This line is unreachable
}

// RecoverExample demonstrates recover catching panic
func RecoverExample() (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("recovered from panic: %v", r)
			fmt.Printf("Recovered: %v\n", r)
		}
	}()

	fmt.Println("Before panic")
	panic("Something went wrong!")
	fmt.Println("This will not be printed")

	return nil
}

// 2. Panic with Values

// PanicWithString panics with string message
func PanicWithString() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("Recovered string: %v\n", r)
		}
	}()

	panic("error occurred")
}

// PanicWithError panics with error
func PanicWithError() {
	defer func() {
		if r := recover(); r != nil {
			if err, ok := r.(error); ok {
				fmt.Printf("Recovered error: %v\n", err)
			}
		}
	}()

	panic(errors.New("something went wrong"))
}

// PanicWithCustomType panics with custom type
type CustomError struct {
	Code    int
	Message string
}

func (e CustomError) Error() string {
	return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}

func PanicWithCustomType() {
	defer func() {
		if r := recover(); r != nil {
			if customErr, ok := r.(CustomError); ok {
				fmt.Printf("Recovered custom error: %v\n", customErr)
			}
		}
	}()

	panic(CustomError{Code: 404, Message: "Not found"})
}

// 3. Panic in Functions

// Divide divides two numbers, panics on division by zero
func Divide(a, b int) int {
	if b == 0 {
		panic("division by zero")
	}
	return a / b
}

// SafeDivide safely divides with panic recovery
func SafeDivide(a, b int) (result int, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("panic recovered: %v", r)
		}
	}()

	result = Divide(a, b)
	return result, nil
}

// 4. Panic in Goroutines

// GoroutineWithPanic demonstrates panic in goroutine
func GoroutineWithPanic() {
	go func() {
		defer func() {
			if r := recover(); r != nil {
				fmt.Printf("Goroutine recovered: %v\n", r)
			}
		}()

		fmt.Println("Goroutine starting")
		panic("goroutine panic")
	}()
}

// 5. Panic with Stack Trace

// PanicWithStackTrace prints stack trace on panic
func PanicWithStackTrace() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("Recovered: %v\n", r)
			fmt.Println("Stack trace:")

			// Get stack trace
			buf := make([]byte, 4096)
			n := runtime.Stack(buf, false)
			fmt.Println(string(buf[:n]))
		}
	}()

	panic("error with stack trace")
}

// GetStackTrace gets current stack trace
func GetStackTrace(skip int) string {
	buf := make([]byte, 4096)
	n := runtime.Stack(buf, false)
	return string(buf[:skip*10 : n]) // Skip some frames
}

// 6. Named Return Values with Recover

// ProcessWithRecover uses named return value with recover
func ProcessWithRecover() (result string, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("panic in ProcessWithRecover: %v", r)
		}
	}()

	// Some processing that might panic
	if somethingBadHappens() {
		panic("processing failed")
	}

	result = "success"
	return result, nil
}

func somethingBadHappens() bool {
	return true
}

// 7. Panic Wrapping

// WrapPanic wraps panic with additional context
func WrapPanic() {
	defer func() {
		if r := recover(); r != nil {
			// Wrap with additional context
			panic(fmt.Errorf("wrapped panic: %w", fmt.Errorf("%v", r)))
		}
	}()

	panic("original panic")
}

// SafeWrapPanic safely wraps panic
func SafeWrapPanic() (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("wrapped: %v", r)
		}
	}()

	panic("original panic")
	return nil
}

// 8. Conditional Panic

// PanicIf panics if condition is true
func PanicIf(condition bool, message string) {
	if condition {
		panic(message)
	}
}

// PanicUnless panics unless condition is true
func PanicUnless(condition bool, message string) {
	if !condition {
		panic(message)
	}
}

// PanicOnError panics if error is not nil
func PanicOnError(err error, message string) {
	if err != nil {
		panic(fmt.Sprintf("%s: %v", message, err))
	}
}

// 9. Panic Handlers

// ErrorHandler handles panics in a reusable way
func ErrorHandler(fn func()) (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("panic caught: %v", r)
		}
	}()

	fn()
	return nil
}

// ExecuteSafely executes function safely with panic recovery
func ExecuteSafely(fn func() error) (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("panic recovered: %v", r)
		}
	}()

	return fn()
}

// 10. Panic with Cleanup

// ProcessWithCleanup processes with cleanup on panic
func ProcessWithCleanup() (err error) {
	// Setup resource
	resource := acquireResource()
	defer func() {
		// Cleanup happens even on panic
		resource.Release()

		if r := recover(); r != nil {
			err = fmt.Errorf("panic during processing: %v", r)
		}
	}()

	// Use resource (might panic)
	resource.Use()

	return nil
}

type Resource struct {
	name string
}

func acquireResource() *Resource {
	return &Resource{name: "resource"}
}

func (r *Resource) Use() {
	fmt.Printf("Using %s\n", r.name)
	// Simulate panic
	panic("resource use failed")
}

func (r *Resource) Release() {
	fmt.Printf("Releasing %s\n", r.name)
}

// 11. Panic Recovery Middleware

// RecoveryMiddleware wraps function with recovery
func RecoveryMiddleware(fn func()) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("Middleware recovered: %v\n", r)
		}
	}()

	fn()
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go Panic/Recover Examples ===\n")

	// 1. Basic panic (would crash without recovery)
	fmt.Println("--- 1. Panic with Recover ---")
	err := RecoverExample()
	if err != nil {
		fmt.Printf("Caught error: %v\n", err)
	}

	// 2. Panic with different values
	fmt.Println("\n--- 2. Panic with Different Values ---")
	PanicWithString()
	PanicWithError()
	PanicWithCustomType()

	// 3. Safe division
	fmt.Println("\n--- 3. Safe Division ---")
	result, err := SafeDivide(10, 2)
	if err == nil {
		fmt.Printf("10 / 2 = %d\n", result)
	}

	result, err = SafeDivide(10, 0)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	}

	// 4. Stack trace
	fmt.Println("\n--- 4. Stack Trace ---")
	PanicWithStackTrace()

	// 5. Error handler
	fmt.Println("\n--- 5. Error Handler ---")
	err = ErrorHandler(func() {
		panic("handled panic")
	})
	if err != nil {
		fmt.Printf("Handler caught: %v\n", err)
	}

	// 6. Execute safely
	fmt.Println("\n--- 6. Execute Safely ---")
	err = ExecuteSafely(func() error {
		panic("safe execution")
	})
	if err != nil {
		fmt.Printf("Safe execution error: %v\n", err)
	}

	// 7. Conditional panic
	fmt.Println("\n--- 7. Conditional Panic ---")
	PanicIf(false, "This won't panic")
	fmt.Println("No panic occurred")

	// 8. Middleware
	fmt.Println("\n--- 8. Recovery Middleware ---")
	RecoveryMiddleware(func() {
		panic("middleware panic")
	})

	// 9. Cleanup on panic
	fmt.Println("\n--- 9. Cleanup on Panic ---")
	ProcessWithCleanup()

	fmt.Println("\n=== All Panic/Recover Examples Completed ===")
}