Exemples de Gestion des Erreurs Web Go

Exemples de gestion des erreurs Web Go incluant la capture d'exceptions, la journalisation et la validation des paramètres

Key Facts

Category
Go
Items
3
Format Families
audio

Sample Overview

Exemples de gestion des erreurs Web Go incluant la capture d'exceptions, la journalisation et la validation des paramètres This sample set belongs to Go and can be used to test related workflows inside Elysia Tools.

💻 Journalisation go

🟢 simple ⭐⭐⭐

Écrire des logs dans des fichiers avec différents niveaux et formats

⏱️ 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 ===")
}

💻 Validation des Paramètres go

🟢 simple ⭐⭐⭐

Valider les paramètres de fonction avec des validateurs personnalisés et des messages d'erreur

⏱️ 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 et Recover go

🟡 intermediate ⭐⭐⭐

Mécanisme de gestion des exceptions panic/recover en Go

⏱️ 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 ===")
}