3.2.4 Fiber 框架入门 #
Fiber 是一个受 Express.js 启发的 Go Web 框架,以其极高的性能和熟悉的 API 设计而著称。本节将深入介绍 Fiber 框架的特点、使用方法和最佳实践。
Fiber 框架特点与优势 #
核心特性 #
1. 极致性能
- 基于 fasthttp 构建,性能优于标准库
- 零内存分配的路由系统
- 高效的内存池管理
2. Express.js 风格 API
- 熟悉的链式调用语法
- 直观的中间件系统
- 简洁的路由定义
3. 丰富的内置功能
- 内置模板引擎支持
- WebSocket 支持
- 静态文件服务
- 压缩和缓存
性能优势 #
// Fiber 的高性能体现在其优化的设计中
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
)
func main() {
// 创建 Fiber 应用
app := fiber.New(fiber.Config{
// 启用预分叉模式以提升性能
Prefork: true,
// 禁用启动消息以减少输出
DisableStartupMessage: true,
// 启用压缩
CompressedFileSuffix: ".gz",
})
// 中间件
app.Use(logger.New())
app.Use(recover.New())
// 路由
app.Get("/", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"message": "Hello, Fiber!",
"fast": true,
})
})
// 启动服务器
app.Listen(":3000")
}
Express.js 风格的 API #
基础路由定义 #
package main
import (
"github.com/gofiber/fiber/v2"
"strconv"
)
func main() {
app := fiber.New()
// GET 路由
app.Get("/", homeHandler)
// POST 路由
app.Post("/users", createUserHandler)
// PUT 路由
app.Put("/users/:id", updateUserHandler)
// DELETE 路由
app.Delete("/users/:id", deleteUserHandler)
// PATCH 路由
app.Patch("/users/:id", patchUserHandler)
// 处理所有 HTTP 方法
app.All("/ping", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"method": c.Method(),
"path": c.Path(),
})
})
// 静态文件服务
app.Static("/", "./public")
app.Static("/api", "./docs")
app.Listen(":3000")
}
func homeHandler(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"message": "Welcome to Fiber API",
"version": "2.0.0",
})
}
func createUserHandler(c *fiber.Ctx) error {
// 解析 JSON 请求体
var user User
if err := c.BodyParser(&user); err != nil {
return c.Status(400).JSON(fiber.Map{
"error": "Cannot parse JSON",
})
}
// 创建用户逻辑
createdUser := createUser(user)
return c.Status(201).JSON(createdUser)
}
路径参数和查询参数 #
func setupParameterHandling(app *fiber.App) {
// 路径参数
app.Get("/users/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
userID, err := strconv.Atoi(id)
if err != nil {
return c.Status(400).JSON(fiber.Map{
"error": "Invalid user ID",
})
}
user := getUserByID(userID)
return c.JSON(user)
})
// 多个路径参数
app.Get("/users/:id/posts/:postId", func(c *fiber.Ctx) error {
userID := c.Params("id")
postID := c.Params("postId")
return c.JSON(fiber.Map{
"user_id": userID,
"post_id": postID,
})
})
// 可选参数
app.Get("/files/:filename?", func(c *fiber.Ctx) error {
filename := c.Params("filename", "default.txt")
return c.JSON(fiber.Map{
"filename": filename,
})
})
// 通配符参数
app.Get("/download/*", func(c *fiber.Ctx) error {
filepath := c.Params("*")
return c.JSON(fiber.Map{
"filepath": filepath,
})
})
// 查询参数
app.Get("/search", func(c *fiber.Ctx) error {
query := c.Query("q")
page := c.Query("page", "1")
size := c.Query("size", "10")
// 获取所有查询参数
queries := c.Queries()
return c.JSON(fiber.Map{
"query": query,
"page": page,
"size": size,
"all": queries,
})
})
// 表单数据
app.Post("/form", func(c *fiber.Ctx) error {
name := c.FormValue("name")
email := c.FormValue("email", "[email protected]")
return c.JSON(fiber.Map{
"name": name,
"email": email,
})
})
}
高性能特性 #
连接池和内存优化 #
func setupHighPerformance() *fiber.App {
app := fiber.New(fiber.Config{
// 启用预分叉模式
Prefork: true,
// 严格路由匹配
StrictRouting: true,
// 启用大小写敏感
CaseSensitive: true,
// 禁用默认日期头
DisableDefaultDate: true,
// 禁用默认内容类型
DisableDefaultContentType: true,
// 禁用头部规范化
DisableHeaderNormalizing: true,
// 自定义错误处理
ErrorHandler: func(c *fiber.Ctx, err error) error {
code := fiber.StatusInternalServerError
if e, ok := err.(*fiber.Error); ok {
code = e.Code
}
return c.Status(code).JSON(fiber.Map{
"error": err.Error(),
})
},
// 读取超时
ReadTimeout: time.Second * 10,
// 写入超时
WriteTimeout: time.Second * 10,
// 空闲超时
IdleTimeout: time.Second * 120,
// 请求体大小限制
BodyLimit: 4 * 1024 * 1024, // 4MB
})
return app
}
缓存和压缩 #
import (
"github.com/gofiber/fiber/v2/middleware/cache"
"github.com/gofiber/fiber/v2/middleware/compress"
"github.com/gofiber/fiber/v2/middleware/etag"
)
func setupOptimizations(app *fiber.App) {
// 启用 ETag
app.Use(etag.New())
// 启用压缩
app.Use(compress.New(compress.Config{
Level: compress.LevelBestSpeed,
}))
// 启用缓存
app.Use(cache.New(cache.Config{
Next: func(c *fiber.Ctx) bool {
// 跳过 POST、PUT、DELETE 请求
return c.Method() != "GET"
},
Expiration: 30 * time.Minute,
CacheControl: true,
}))
// 静态文件缓存
app.Static("/static", "./public", fiber.Static{
Compress: true,
ByteRange: true,
Browse: false,
CacheDuration: 24 * time.Hour,
})
}
中间件与路由组 #
内置中间件 #
import (
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/helmet"
"github.com/gofiber/fiber/v2/middleware/limiter"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/monitor"
"github.com/gofiber/fiber/v2/middleware/pprof"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/fiber/v2/middleware/requestid"
)
func setupBuiltinMiddleware(app *fiber.App) {
// 恢复中间件
app.Use(recover.New())
// 请求 ID 中间件
app.Use(requestid.New())
// 日志中间件
app.Use(logger.New(logger.Config{
Format: `{"time":"${time}","method":"${method}","path":"${path}","status":${status},"latency":"${latency}","ip":"${ip}","user_agent":"${ua}"}` + "\n",
}))
// CORS 中间件
app.Use(cors.New(cors.Config{
AllowOrigins: "*",
AllowMethods: "GET,POST,HEAD,PUT,DELETE,PATCH",
AllowHeaders: "Origin, Content-Type, Accept, Authorization",
}))
// 安全中间件
app.Use(helmet.New())
// 限流中间件
app.Use(limiter.New(limiter.Config{
Max: 100,
Expiration: 1 * time.Minute,
KeyGenerator: func(c *fiber.Ctx) string {
return c.IP()
},
LimitReached: func(c *fiber.Ctx) error {
return c.Status(429).JSON(fiber.Map{
"error": "Rate limit exceeded",
})
},
}))
// 性能监控(仅开发环境)
if fiber.IsChild() {
app.Get("/metrics", monitor.New())
app.Use(pprof.New())
}
}
自定义中间件 #
// JWT 认证中间件
func jwtMiddleware(secret string) fiber.Handler {
return func(c *fiber.Ctx) error {
token := c.Get("Authorization")
if token == "" {
return c.Status(401).JSON(fiber.Map{
"error": "Missing authorization header",
})
}
// 移除 "Bearer " 前缀
if len(token) > 7 && token[:7] == "Bearer " {
token = token[7:]
}
// 验证 JWT token
claims, err := validateJWT(token, secret)
if err != nil {
return c.Status(401).JSON(fiber.Map{
"error": "Invalid token",
})
}
// 将用户信息存储到上下文
c.Locals("user", claims)
return c.Next()
}
}
// API 密钥中间件
func apiKeyMiddleware(validKeys []string) fiber.Handler {
keyMap := make(map[string]bool)
for _, key := range validKeys {
keyMap[key] = true
}
return func(c *fiber.Ctx) error {
apiKey := c.Get("X-API-Key")
if apiKey == "" {
return c.Status(401).JSON(fiber.Map{
"error": "API key required",
})
}
if !keyMap[apiKey] {
return c.Status(401).JSON(fiber.Map{
"error": "Invalid API key",
})
}
return c.Next()
}
}
// 请求验证中间件
func validationMiddleware() fiber.Handler {
return func(c *fiber.Ctx) error {
// 验证 Content-Type
if c.Method() == "POST" || c.Method() == "PUT" {
contentType := c.Get("Content-Type")
if !strings.Contains(contentType, "application/json") {
return c.Status(400).JSON(fiber.Map{
"error": "Content-Type must be application/json",
})
}
}
return c.Next()
}
}
// 审计日志中间件
func auditMiddleware() fiber.Handler {
return func(c *fiber.Ctx) error {
start := time.Now()
// 记录请求体
var requestBody string
if c.Method() == "POST" || c.Method() == "PUT" {
requestBody = string(c.Body())
}
// 继续处理请求
err := c.Next()
// 记录审计信息
auditLog := map[string]interface{}{
"timestamp": start,
"method": c.Method(),
"path": c.Path(),
"ip": c.IP(),
"user_agent": c.Get("User-Agent"),
"request_body": requestBody,
"status_code": c.Response().StatusCode(),
"duration": time.Since(start),
}
// 异步记录日志
go logAudit(auditLog)
return err
}
}
路由组 #
func setupRouteGroups(app *fiber.App) {
// API v1 路由组
v1 := app.Group("/api/v1")
// 用户路由组
users := v1.Group("/users")
users.Get("/", getUsersHandler)
users.Post("/", createUserHandler)
users.Get("/:id", getUserHandler)
users.Put("/:id", updateUserHandler)
users.Delete("/:id", deleteUserHandler)
// 用户子资源
users.Get("/:id/posts", getUserPostsHandler)
users.Post("/:id/posts", createUserPostHandler)
// 文章路由组
posts := v1.Group("/posts")
posts.Get("/", getPostsHandler)
posts.Post("/", createPostHandler)
posts.Get("/:id", getPostHandler)
posts.Put("/:id", updatePostHandler)
posts.Delete("/:id", deletePostHandler)
// 管理员路由组(需要认证)
admin := app.Group("/admin")
admin.Use(jwtMiddleware("your-secret-key"))
admin.Use(func(c *fiber.Ctx) error {
user := c.Locals("user").(map[string]interface{})
if user["role"] != "admin" {
return c.Status(403).JSON(fiber.Map{
"error": "Admin access required",
})
}
return c.Next()
})
admin.Get("/dashboard", adminDashboardHandler)
admin.Get("/users", adminGetUsersHandler)
admin.Delete("/users/:id", adminDeleteUserHandler)
// 公共 API(需要 API 密钥)
public := app.Group("/public")
public.Use(apiKeyMiddleware([]string{"key1", "key2", "key3"}))
public.Get("/stats", getPublicStatsHandler)
public.Get("/health", getHealthHandler)
}
数据绑定和验证 #
请求数据处理 #
type User struct {
ID int `json:"id"`
Name string `json:"name" validate:"required,min=2,max=50"`
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"required,min=18,max=120"`
Password string `json:"password,omitempty" validate:"required,min=8"`
}
type CreateUserRequest struct {
Name string `json:"name" validate:"required,min=2,max=50"`
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"required,min=18,max=120"`
Password string `json:"password" validate:"required,min=8"`
}
func setupDataBinding(app *fiber.App) {
// JSON 数据绑定
app.Post("/users", func(c *fiber.Ctx) error {
var req CreateUserRequest
// 解析 JSON 请求体
if err := c.BodyParser(&req); err != nil {
return c.Status(400).JSON(fiber.Map{
"error": "Invalid JSON format",
})
}
// 验证数据
if err := validateStruct(req); err != nil {
return c.Status(400).JSON(fiber.Map{
"error": "Validation failed",
"details": err.Error(),
})
}
// 创建用户
user := createUser(req)
return c.Status(201).JSON(user)
})
// 查询参数绑定
app.Get("/users", func(c *fiber.Ctx) error {
page := c.QueryInt("page", 1)
size := c.QueryInt("size", 10)
sort := c.Query("sort", "id")
// 验证参数
if page < 1 {
page = 1
}
if size < 1 || size > 100 {
size = 10
}
users := getUsersWithPagination(page, size, sort)
return c.JSON(fiber.Map{
"data": users,
"page": page,
"size": size,
"total": getTotalUsers(),
})
})
// 表单数据绑定
app.Post("/form", func(c *fiber.Ctx) error {
var form struct {
Name string `form:"name"`
Email string `form:"email"`
Age int `form:"age"`
}
if err := c.BodyParser(&form); err != nil {
return c.Status(400).JSON(fiber.Map{
"error": "Invalid form data",
})
}
return c.JSON(form)
})
// 文件上传
app.Post("/upload", func(c *fiber.Ctx) error {
file, err := c.FormFile("file")
if err != nil {
return c.Status(400).JSON(fiber.Map{
"error": "No file uploaded",
})
}
// 验证文件大小
if file.Size > 10*1024*1024 { // 10MB
return c.Status(400).JSON(fiber.Map{
"error": "File too large",
})
}
// 保存文件
filename := fmt.Sprintf("uploads/%d_%s", time.Now().Unix(), file.Filename)
if err := c.SaveFile(file, filename); err != nil {
return c.Status(500).JSON(fiber.Map{
"error": "Failed to save file",
})
}
return c.JSON(fiber.Map{
"message": "File uploaded successfully",
"filename": filename,
"size": file.Size,
})
})
}
WebSocket 支持 #
import (
"github.com/gofiber/websocket/v2"
)
func setupWebSocket(app *fiber.App) {
// WebSocket 升级中间件
app.Use("/ws", func(c *fiber.Ctx) error {
if websocket.IsWebSocketUpgrade(c) {
c.Locals("allowed", true)
return c.Next()
}
return fiber.ErrUpgradeRequired
})
// WebSocket 连接处理
app.Get("/ws/:id", websocket.New(func(c *websocket.Conn) {
// 获取连接参数
userID := c.Params("id")
// 连接管理
clients[userID] = c
defer delete(clients, userID)
// 发送欢迎消息
c.WriteJSON(fiber.Map{
"type": "welcome",
"message": "Connected successfully",
"user_id": userID,
})
// 消息处理循环
for {
var msg map[string]interface{}
if err := c.ReadJSON(&msg); err != nil {
break
}
// 处理不同类型的消息
switch msg["type"] {
case "chat":
broadcastMessage(msg)
case "ping":
c.WriteJSON(fiber.Map{"type": "pong"})
default:
c.WriteJSON(fiber.Map{
"type": "error",
"error": "Unknown message type",
})
}
}
}))
// 广播消息的 HTTP 接口
app.Post("/broadcast", func(c *fiber.Ctx) error {
var msg map[string]interface{}
if err := c.BodyParser(&msg); err != nil {
return c.Status(400).JSON(fiber.Map{
"error": "Invalid message format",
})
}
broadcastMessage(msg)
return c.JSON(fiber.Map{
"message": "Message broadcasted",
})
})
}
var clients = make(map[string]*websocket.Conn)
func broadcastMessage(msg map[string]interface{}) {
for userID, conn := range clients {
if err := conn.WriteJSON(msg); err != nil {
// 连接已断开,清理
delete(clients, userID)
}
}
}
完整示例应用 #
package main
import (
"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"
"strconv"
"time"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
var users = []User{
{ID: 1, Name: "John Doe", Email: "[email protected]", CreatedAt: time.Now(), UpdatedAt: time.Now()},
{ID: 2, Name: "Jane Smith", Email: "[email protected]", CreatedAt: time.Now(), UpdatedAt: time.Now()},
}
func main() {
// 创建 Fiber 应用
app := fiber.New(fiber.Config{
ErrorHandler: func(c *fiber.Ctx, err error) error {
code := fiber.StatusInternalServerError
if e, ok := err.(*fiber.Error); ok {
code = e.Code
}
return c.Status(code).JSON(fiber.Map{
"error": err.Error(),
})
},
})
// 中间件
app.Use(recover.New())
app.Use(logger.New())
app.Use(cors.New())
// 路由设置
setupRoutes(app)
// 启动服务器
app.Listen(":3000")
}
func setupRoutes(app *fiber.App) {
// 健康检查
app.Get("/health", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"status": "ok",
"time": time.Now(),
})
})
// API 路由组
api := app.Group("/api/v1")
// 用户相关路由
api.Get("/users", getUsers)
api.Get("/users/:id", getUser)
api.Post("/users", createUser)
api.Put("/users/:id", updateUser)
api.Delete("/users/:id", deleteUser)
}
func getUsers(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"data": users,
"total": len(users),
})
}
func getUser(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("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",
})
}
func createUser(c *fiber.Ctx) error {
var user User
if err := c.BodyParser(&user); err != nil {
return c.Status(400).JSON(fiber.Map{
"error": "Invalid request format",
})
}
user.ID = len(users) + 1
user.CreatedAt = time.Now()
user.UpdatedAt = time.Now()
users = append(users, user)
return c.Status(201).JSON(user)
}
func updateUser(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return c.Status(400).JSON(fiber.Map{
"error": "Invalid user ID",
})
}
var updatedUser User
if err := c.BodyParser(&updatedUser); err != nil {
return c.Status(400).JSON(fiber.Map{
"error": "Invalid request format",
})
}
for i, user := range users {
if user.ID == id {
updatedUser.ID = id
updatedUser.CreatedAt = user.CreatedAt
updatedUser.UpdatedAt = time.Now()
users[i] = updatedUser
return c.JSON(updatedUser)
}
}
return c.Status(404).JSON(fiber.Map{
"error": "User not found",
})
}
func deleteUser(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("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.JSON(fiber.Map{
"message": "User deleted successfully",
})
}
}
return c.Status(404).JSON(fiber.Map{
"error": "User not found",
})
}
通过本节的学习,你已经掌握了 Fiber 框架的核心特性和使用方法。Fiber 以其极高的性能和熟悉的 Express.js 风格 API,为 Go Web 开发提供了强大而高效的解决方案。在实际项目中,可以充分利用 Fiber 的高性能特性和丰富的中间件生态,构建快速、可靠的 Web 应用。