Fiber 示例

Fiber Go Web 框架示例,包括路由、中间件、模板、WebSocket、数据库集成和性能优化

💻 Fiber 你好世界 go

🟢 simple

基础 Fiber Web 服务器设置,包含路由、中间件、处理器和基本的 Go Web 开发模式

// Fiber Hello World Examples

package main

import (
    "fmt"
    "log"
    "time"

    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/cors"
    "github.com/gofiber/fiber/v2/middleware/logger"
    "github.com/gofiber/fiber/v2/middleware/recover"
)

// 1. Basic Fiber Server
func basicServer() {
    app := fiber.New()

    app.Get("/", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{
            "message": "Hello, Fiber!",
            "time":    time.Now(),
        })
    })

    log.Fatal(app.Listen(":3000"))
}

// 2. Fiber with Middleware
func serverWithMiddleware() {
    app := fiber.New(fiber.Config{
        // Prefork enables multiple processes to handle requests
        Prefork: true,
    })

    // Middleware
    app.Use(recover.New())           // Recover from panics
    app.Use(logger.New())            // HTTP request logger
    app.Use(cors.New())              // Enable CORS

    // Custom middleware
    app.Use(func(c *fiber.Ctx) error {
        c.Set("X-Server", "Fiber")
        return c.Next()
    })

    // Timing middleware
    app.Use(func(c *fiber.Ctx) error {
        start := time.Now()
        err := c.Next()
        duration := time.Since(start)

        c.Set("X-Response-Time", duration.String())
        return err
    })

    app.Get("/", func(c *fiber.Ctx) error {
        return c.SendString("Hello from Fiber with middleware!")
    })

    log.Fatal(app.Listen(":3000"))
}

// 3. REST API with Fiber
type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
    Age   int    `json:"age"`
}

// In-memory database (use proper database in production)
var users = []User{
    {ID: 1, Name: "John Doe", Email: "[email protected]", Age: 30},
    {ID: 2, Name: "Jane Smith", Email: "[email protected]", Age: 25},
    {ID: 3, Name: "Bob Johnson", Email: "[email protected]", Age: 35},
}

func restAPI() {
    app := fiber.New()

    // Get all users
    app.Get("/api/users", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{
            "users": users,
            "total": len(users),
        })
    })

    // Get user by ID
    app.Get("/api/users/:id", func(c *fiber.Ctx) error {
        id, err := c.ParamsInt("id")
        if err != nil {
            return c.Status(400).JSON(fiber.Map{
                "error": "Invalid user ID",
            })
        }

        for _, user := range users {
            if user.ID == id {
                return c.JSON(user)
            }
        }

        return c.Status(404).JSON(fiber.Map{
            "error": "User not found",
        })
    })

    // Create new user
    app.Post("/api/users", func(c *fiber.Ctx) error {
        user := new(User)
        if err := c.BodyParser(user); err != nil {
            return c.Status(400).JSON(fiber.Map{
                "error": "Cannot parse JSON",
            })
        }

        // Validation
        if user.Name == "" || user.Email == "" {
            return c.Status(400).JSON(fiber.Map{
                "error": "Name and email are required",
            })
        }

        user.ID = len(users) + 1
        users = append(users, *user)

        return c.Status(201).JSON(user)
    })

    // Update user
    app.Put("/api/users/:id", func(c *fiber.Ctx) error {
        id, err := c.ParamsInt("id")
        if err != nil {
            return c.Status(400).JSON(fiber.Map{
                "error": "Invalid user ID",
            })
        }

        var updateUser User
        if err := c.BodyParser(&updateUser); err != nil {
            return c.Status(400).JSON(fiber.Map{
                "error": "Cannot parse JSON",
            })
        }

        for i, user := range users {
            if user.ID == id {
                if updateUser.Name != "" {
                    users[i].Name = updateUser.Name
                }
                if updateUser.Email != "" {
                    users[i].Email = updateUser.Email
                }
                if updateUser.Age > 0 {
                    users[i].Age = updateUser.Age
                }
                return c.JSON(users[i])
            }
        }

        return c.Status(404).JSON(fiber.Map{
            "error": "User not found",
        })
    })

    // Delete user
    app.Delete("/api/users/:id", func(c *fiber.Ctx) error {
        id, err := c.ParamsInt("id")
        if err != nil {
            return c.Status(400).JSON(fiber.Map{
                "error": "Invalid user ID",
            })
        }

        for i, user := range users {
            if user.ID == id {
                users = append(users[:i], users[i+1:]...)
                return c.SendStatus(204)
            }
        }

        return c.Status(404).JSON(fiber.Map{
            "error": "User not found",
        })
    })

    log.Fatal(app.Listen(":3000"))
}

// 4. Fiber with Grouped Routes and Middleware
func groupedRoutes() {
    app := fiber.New()

    // API v1 group with middleware
    v1 := app.Group("/api/v1", func(c *fiber.Ctx) error {
        c.Set("API-Version", "1.0")
        return c.Next()
    })

    // Users group
    users := v1.Group("/users")
    users.Get("/", func(c *fiber.Ctx) error {
        return c.JSON(users)
    })

    users.Post("/", func(c *fiber.Ctx) error {
        user := new(User)
        if err := c.BodyParser(user); err != nil {
            return c.Status(400).JSON(fiber.Map{"error": err.Error()})
        }
        user.ID = len(users) + 1
        users = append(users, *user)
        return c.Status(201).JSON(user)
    })

    // Admin group with authentication middleware
    admin := v1.Group("/admin", func(c *fiber.Ctx) error {
        token := c.Get("Authorization")
        if token != "Bearer admin-token" {
            return c.Status(401).JSON(fiber.Map{"error": "Unauthorized"})
        }
        return c.Next()
    })

    admin.Get("/stats", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{
            "total_users": len(users),
            "server_time": time.Now(),
        })
    })

    log.Fatal(app.Listen(":3000"))
}

// 5. Fiber with Static Files and Templates
import "github.com/gofiber/template/html/v2"

func staticFilesAndTemplates() {
    // Initialize template engine
    engine := html.New("./views", ".html")

    app := fiber.New(fiber.Config{
        Views: engine,
    })

    // Serve static files
    app.Static("/", "./public")

    // Template route
    app.Get("/welcome", func(c *fiber.Ctx) error {
        return c.Render("welcome", fiber.Map{
            "Title": "Welcome to Fiber",
            "Name":  "Developer",
        })
    })

    // Dynamic template
    app.Get("/user/:name", func(c *fiber.Ctx) error {
        return c.Render("user", fiber.Map{
            "Name": c.Params("name"),
            "Time": time.Now(),
        })
    })

    log.Fatal(app.Listen(":3000"))
}

// 6. Fiber with WebSocket Support
import "github.com/gofiber/websocket/v2"

func websocketExample() {
    app := fiber.New()

    // WebSocket middleware
    app.Use("/ws", func(c *fiber.Ctx) error {
        // IsWebSocketUpgrade returns true if the client
        // requested upgrade to the WebSocket protocol.
        if websocket.IsWebSocketUpgrade(c) {
            c.Locals("allowed", true)
            return c.Next()
        }
        return fiber.ErrUpgradeRequired
    })

    // WebSocket route
    app.Get("/ws/:id", websocket.New(func(c *websocket.Conn) {
        // c.Locals is added to the *websocket.Conn
        log.Println(c.Locals("allowed"))  // true
        log.Println(c.Params("id"))       // 123

        var (
            mt  int
            msg []byte
            err error
        )
        for {
            if mt, msg, err = c.ReadMessage(); err != nil {
                log.Println("read:", err)
                break
            }
            log.Printf("recv: %s", msg)

            if err = c.WriteMessage(mt, msg); err != nil {
                log.Println("write:", err)
                break
            }
        }
    }))

    // HTTP route
    app.Get("/", func(c *fiber.Ctx) error {
        return c.SendString("WebSocket example. Connect to ws://localhost:3000/ws/123")
    })

    log.Fatal(app.Listen(":3000"))
}

// 7. Fiber with Database Integration (GORM)
import (
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

type Product struct {
    gorm.Model
    Name        string  `json:"name"`
    Description string  `json:"description"`
    Price       float64 `json:"price"`
    Stock       int     `json:"stock"`
}

func databaseIntegration() {
    app := fiber.New()

    // Initialize database
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
    if err != nil {
        panic("Failed to connect database")
    }

    // Auto migrate the schema
    db.AutoMigrate(&Product{})

    // Seed data
    db.Create(&Product{Name: "Laptop", Description: "High-performance laptop", Price: 999.99, Stock: 50})
    db.Create(&Product{Name: "Mouse", Description: "Wireless optical mouse", Price: 29.99, Stock: 200})

    // API Routes
    app.Get("/api/products", func(c *fiber.Ctx) error {
        var products []Product
        db.Find(&products)
        return c.JSON(products)
    })

    app.Get("/api/products/:id", func(c *fiber.Ctx) error {
        id := c.Params("id")
        var product Product
        result := db.First(&product, id)
        if result.Error != nil {
            return c.Status(404).JSON(fiber.Map{"error": "Product not found"})
        }
        return c.JSON(product)
    })

    app.Post("/api/products", func(c *fiber.Ctx) error {
        product := new(Product)
        if err := c.BodyParser(product); err != nil {
            return c.Status(400).JSON(fiber.Map{"error": err.Error()})
        }

        result := db.Create(product)
        if result.Error != nil {
            return c.Status(400).JSON(fiber.Map{"error": result.Error.Error()})
        }

        return c.Status(201).JSON(product)
    })

    app.Put("/api/products/:id", func(c *fiber.Ctx) error {
        id := c.Params("id")
        var product Product
        result := db.First(&product, id)
        if result.Error != nil {
            return c.Status(404).JSON(fiber.Map{"error": "Product not found"})
        }

        if err := c.BodyParser(&product); err != nil {
            return c.Status(400).JSON(fiber.Map{"error": err.Error()})
        }

        db.Save(&product)
        return c.JSON(product)
    })

    app.Delete("/api/products/:id", func(c *fiber.Ctx) error {
        id := c.Params("id")
        var product Product
        result := db.Delete(&product, id)
        if result.Error != nil {
            return c.Status(404).JSON(fiber.Map{"error": "Product not found"})
        }
        return c.SendStatus(204)
    })

    log.Fatal(app.Listen(":3000"))
}

// 8. Fiber with File Upload
import (
    "os"
    "path/filepath"
)

func fileUpload() {
    app := fiber.New()

    // Ensure upload directory exists
    os.MkdirAll("./uploads", 0755)

    // Single file upload
    app.Post("/upload", func(c *fiber.Ctx) error {
        file, err := c.FormFile("document")
        if err != nil {
            return c.Status(400).JSON(fiber.Map{"error": "No file uploaded"})
        }

        // Save file to uploads directory
        filename := filepath.Base(file.Filename)
        savePath := filepath.Join("./uploads", filename)

        if err := c.SaveFile(file, savePath); err != nil {
            return c.Status(500).JSON(fiber.Map{"error": "Cannot save file"})
        }

        return c.JSON(fiber.Map{
            "message":  "File uploaded successfully",
            "filename": filename,
            "size":     file.Size,
            "path":     savePath,
        })
    })

    // Multiple files upload
    app.Post("/upload/multiple", func(c *fiber.Ctx) error {
        form, err := c.MultipartForm()
        if err != nil {
            return c.Status(400).JSON(fiber.Map{"error": "Cannot parse form"})
        }

        files := form.File["documents"]
        var uploadedFiles []string

        for _, file := range files {
            filename := filepath.Base(file.Filename)
            savePath := filepath.Join("./uploads", filename)

            if err := c.SaveFile(file, savePath); err != nil {
                return c.Status(500).JSON(fiber.Map{
                    "error":   "Cannot save file: " + filename,
                    "file":    filename,
                    "message": "Error occurred during file upload",
                })
            }

            uploadedFiles = append(uploadedFiles, filename)
        }

        return c.JSON(fiber.Map{
            "message": "Files uploaded successfully",
            "files":   uploadedFiles,
            "count":   len(uploadedFiles),
        })
    })

    // List uploaded files
    app.Get("/files", func(c *fiber.Ctx) error {
        files, err := os.ReadDir("./uploads")
        if err != nil {
            return c.Status(500).JSON(fiber.Map{"error": "Cannot read uploads directory"})
        }

        var fileList []string
        for _, file := range files {
            fileList = append(fileList, file.Name())
        }

        return c.JSON(fiber.Map{
            "files": fileList,
            "count": len(fileList),
        })
    })

    // Download file
    app.Get("/download/:filename", func(c *fiber.Ctx) error {
        filename := c.Params("filename")
        filePath := filepath.Join("./uploads", filename)

        return c.Download(filePath, filename)
    })

    log.Fatal(app.Listen(":3000"))
}

// 9. Fiber with Authentication and JWT
import (
    "strings"
    "time"

    "github.com/golang-jwt/jwt/v5"
    "github.com/gofiber/fiber/v2/middleware/keyauth"
)

// JWT Claims
type Claims struct {
    UserID   int    `json:"user_id"`
    Username string `json:"username"`
    jwt.RegisteredClaims
}

var jwtSecret = []byte("your-secret-key")

// Mock users
var mockUsers = map[string]string{
    "admin":  "admin123",
    "user":   "user123",
}

func authenticationJWT() {
    app := fiber.New()

    // Login endpoint
    app.Post("/login", func(c *fiber.Ctx) error {
        type LoginRequest struct {
            Username string `json:"username"`
            Password string `json:"password"`
        }

        login := new(LoginRequest)
        if err := c.BodyParser(login); err != nil {
            return c.Status(400).JSON(fiber.Map{"error": "Cannot parse JSON"})
        }

        // Validate credentials (in production, use proper password hashing)
        if password, exists := mockUsers[login.Username]; !exists || password != login.Password {
            return c.Status(401).JSON(fiber.Map{"error": "Invalid credentials"})
        }

        // Generate JWT token
        expirationTime := time.Now().Add(24 * time.Hour)
        claims := &Claims{
            UserID:   1,
            Username: login.Username,
            RegisteredClaims: jwt.RegisteredClaims{
                ExpiresAt: jwt.NewNumericDate(expirationTime),
            },
        }

        token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
        tokenString, err := token.SignedString(jwtSecret)
        if err != nil {
            return c.Status(500).JSON(fiber.Map{"error": "Could not generate token"})
        }

        return c.JSON(fiber.Map{
            "token":     tokenString,
            "expiresAt": expirationTime,
            "user": fiber.Map{
                "username": login.Username,
                "userId":   1,
            },
        })
    })

    // JWT validation middleware
    app.Use(keyauth.New(keyauth.Config{
        KeyLookup: "header:Authorization",
        Validator: func(c *fiber.Ctx, key string) (bool, error) {
            if !strings.HasPrefix(key, "Bearer ") {
                return false, nil
            }

            tokenString := key[7:] // Remove "Bearer " prefix
            claims := &Claims{}

            token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
                return jwtSecret, nil
            })

            if err != nil || !token.Valid {
                return false, nil
            }

            // Store claims in context
            c.Locals("user", claims)
            return true, nil
        },
    }))

    // Protected routes
    app.Get("/profile", func(c *fiber.Ctx) error {
        claims := c.Locals("user").(*Claims)
        return c.JSON(fiber.Map{
            "user_id":  claims.UserID,
            "username": claims.Username,
            "message": "This is protected content",
        })
    })

    // API group
    api := app.Group("/api")
    api.Get("/data", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{
            "message": "This is API data",
            "time":    time.Now(),
        })
    })

    log.Fatal(app.Listen(":3000"))
}

// 10. Fiber Production Best Practices
func productionSetup() {
    app := fiber.New(fiber.Config{
        Prefork:       true,              // Enable prefork
        CaseSensitive: true,              // Case sensitive routing
        StrictRouting: true,              // Strict routing
        ServerHeader:  "Fiber",          // Server header
        AppName:       "Fiber App v1.0", // App name
        // Read timeout
        ReadTimeout: 5 * time.Second,
        // Write timeout
        WriteTimeout: 10 * time.Second,
        // Idle timeout
        IdleTimeout: 60 * time.Second,
    })

    // Security middleware
    app.Use(recover.New())
    app.Use(cors.New(cors.Config{
        AllowOrigins:     "https://example.com",
        AllowMethods:     "GET,POST,PUT,DELETE,OPTIONS",
        AllowHeaders:     "Origin, Content-Type, Accept, Authorization",
        ExposeHeaders:    "Content-Length",
        AllowCredentials: true,
        MaxAge:           86400,
    }))

    // Rate limiting
    app.Use(func(c *fiber.Ctx) error {
        // Simple rate limiting (use proper rate limiting in production)
        // This is just an example
        c.Set("X-RateLimit-Limit", "1000")
        c.Set("X-RateLimit-Remaining", "999")
        return c.Next()
    })

    // Health check endpoint
    app.Get("/health", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{
            "status":    "ok",
            "timestamp": time.Now(),
            "version":   "1.0.0",
            "uptime":    time.Since(time.Now()).String(),
        })
    })

    // Readiness check for Kubernetes
    app.Get("/ready", func(c *fiber.Ctx) error {
        // Check database connectivity, external services, etc.
        return c.JSON(fiber.Map{
            "status": "ready",
        })
    })

    // Liveness check for Kubernetes
    app.Get("/live", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{
            "status": "alive",
        })
    })

    // Metrics endpoint
    app.Get("/metrics", func(c *fiber.Ctx) error {
        // Return Prometheus-compatible metrics
        metrics := `# HELP http_requests_total The total number of HTTP requests
# TYPE http_requests_total counter
http_requests_total 1234

# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 12.34
`
        c.Set("Content-Type", "text/plain")
        return c.SendString(metrics)
    })

    // Graceful shutdown
    go func() {
        <-time.After(30 * time.Second)
        fmt.Println("Shutting down server...")
        app.Shutdown()
    }()

    log.Fatal(app.Listen(":3000"))
}

func main() {
    // Uncomment the function you want to run
    // basicServer()
    // serverWithMiddleware()
    // restAPI()
    // groupedRoutes()
    // staticFilesAndTemplates()
    // websocketExample()
    // databaseIntegration()
    // fileUpload()
    // authenticationJWT()
    // productionSetup()

    // Default to basic server
    basicServer()
}