構建 Go RESTful API 的終極指南:Fiber、GORM 和 PostgreSQL

本文將帶你一步步構建一個基於 Go、Fiber 和 GORM 的 RESTful API,並使用 PostgreSQL 作爲數據庫。我們將涵蓋從項目搭建、數據庫配置到 API 路由、認證、授權、錯誤處理等關鍵環節,並附有完整代碼示例和詳細解釋,幫助你快速上手 Go RESTful API 開發。

項目搭建

首先,我們需要創建一個新的 Go 項目並安裝必要的依賴包。

# 初始化 Go 模塊
go mod init your-project-name

# 安裝依賴
go get github.com/gofiber/fiber/v2
go get github.com/jinzhu/gorm
go get github.com/joho/godotenv
go get github.com/spf13/viper
go get github.com/swaggo/swag
go get github.com/stretchr/testify
go get github.com/go-playground/validator/v10
go get github.com/sirupsen/logrus

數據庫配置

  1. 創建 PostgreSQL 數據庫: 使用你喜歡的數據庫管理工具(例如 pgAdmin)創建名爲 fiberdb 的數據庫,並創建一個名爲 postgres 的用戶,並賦予其對 fiberdb 數據庫的讀寫權限。

  2. 配置環境變量: 創建一個 .env 文件用於存儲數據庫配置信息。

# .env
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=your_password
DB_NAME=fiberdb
  1. 連接數據庫: 在你的 main.go 文件中,使用 gorm 庫連接到 PostgreSQL 數據庫。
package main

import (
   "fmt"
   "log"
   "os"

   "github.com/joho/godotenv"
   "github.com/jinzhu/gorm"
   _ "github.com/jinzhu/gorm/dialects/postgres" // 導入 PostgreSQL 驅動
)

func main() {
   // 加載環境變量
   err := godotenv.Load()
   if err != nil {
       log.Fatal("Error loading .env file")
   }

   // 連接數據庫
   db, err := gorm.Open("postgres", fmt.Sprintf("host=%s port=%s user=%s password=%s db,
       os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME")))
   if err != nil {
       log.Fatal("Error connecting to database:", err)
   }
   defer db.Close()

   // ... 你的其他代碼 ...
}

模型定義

定義你的數據庫模型。例如,創建一個 User 模型:

package model

import (
    "time"

    "github.com/jinzhu/gorm"
)

type User struct {
    gorm.Model
    Name     string    `json:"name" gorm:"not null"`
    Email    string    `json:"email" gorm:"not null;unique"`
    Password string    `json:"password" gorm:"not null"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

遷移數據庫

使用 gorm 的遷移功能來創建數據庫表。

package main

import (
    "fmt"
    "log"
    "os"

    "github.com/joho/godotenv"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/postgres"
    "your-project-name/model"
)

func main() {
    // ... (數據庫連接代碼) ...

    // 自動遷移
    err := db.AutoMigrate(&model.User{}).Error
    if err != nil {
        log.Fatal("Error migrating database:", err)
    }

    // ... (你的其他代碼) ...
}

使用 Fiber 創建 API 路由

  1. 創建路由處理器: 創建一個 handler 包,用於處理 API 請求。
package handler

import (
   "github.com/gofiber/fiber/v2"
   "your-project-name/model"
)

func CreateUser(c *fiber.Ctx) error {
   // ... (處理創建用戶邏輯) ...
}

func GetUser(c *fiber.Ctx) error {
   // ... (處理獲取用戶邏輯) ...
}
  1. 定義 API 路由:main.go 文件中使用 fiber 創建路由。
package main

import (
   "log"
   "os"

   "github.com/gofiber/fiber/v2"
   "github.com/joho/godotenv"
   "github.com/jinzhu/gorm"
   _ "github.com/jinzhu/gorm/dialects/postgres"
   "your-project-name/handler"
   "your-project-name/model"
)

func main() {
   // ... (數據庫連接代碼) ...

   // 創建 Fiber 應用
   app := fiber.New()

   // 定義路由
   app.Post("/users", handler.CreateUser)
   app.Get("/users/:id", handler.GetUser)

   // 啓動服務器
   log.Fatal(app.Listen(fmt.Sprintf(":%s", os.Getenv("APP_PORT"))))
}

認證和授權

  1. JWT 認證: 使用 JWT 作爲認證機制,並創建 token 包來生成和驗證 JWT。
package token

import (
   "fmt"
   "os"
   "time"

   "github.com/dgrijalva/jwt-go"
)

// 定義 JWT 聲明結構
type Claims struct {
   UserID uint   `json:"user_id"`
   Role   string `json:"role"`
   jwt.StandardClaims
}

// 生成 JWT
func GenerateToken(userID uint, role string) (string, error) {
   // 創建 JWT 聲明
   claims := &Claims{
       UserID: userID,
       Role:   role,
       StandardClaims: jwt.StandardClaims{
           ExpiresAt: time.Now().Add(time.Minute * 30).Unix(), // 設置過期時間
           Issuer:    "your-project-name",
       },
   }

   // 使用 JWT 簽名密鑰生成 JWT
   token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
   return token.SignedString([]byte(os.Getenv("JWT_SECRET")))
}

// 驗證 JWT
func ValidateToken(tokenString string) (*Claims, error) {
   // 解析 JWT
   token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
       return []byte(os.Getenv("JWT_SECRET")), nil
   })
   if err != nil {
       return nil, err
   }

   // 驗證 JWT 聲明
   if claims, ok := token.Claims.(*Claims); ok && token.Valid {
       return claims, nil
   } else {
       return nil, fmt.Errorf("invalid token")
   }
}
  1. 創建認證中間件: 創建一個 middleware 包,用於處理認證邏輯。
package middleware

import (
   "fmt"
   "strings"

   "github.com/gofiber/fiber/v2"
   "your-project-name/token"
)

func Auth(c *fiber.Ctx) error {
   // 獲取 Authorization 頭信息
   authHeader := c.Get("Authorization")
   if authHeader == "" {
       return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message""Missing Authorization header"})
   }

   // 驗證 JWT
   tokenString := strings.Split(authHeader, " ")[1]
   claims, err := token.ValidateToken(tokenString)
   if err != nil {
       return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message""Invalid token"})
   }

   // 將用戶 ID 和角色信息添加到上下文
   c.Locals("user_id", claims.UserID)
   c.Locals("role", claims.Role)

   return c.Next()
}
  1. 使用認證中間件: 在需要認證的路由上使用 Auth 中間件。
app.Post("/users", middleware.Auth, handler.CreateUser)
  1. 授權: 使用 roles 包定義角色和權限。
package roles

const (
   AdminRole   = "admin"
   UserRole    = "user"
   ManageUsers = "manage_users"
)

// 定義角色權限映射
var RolePermissions = map[string][]string{
   AdminRole: {ManageUsers},
   UserRole: {},
}
  1. 創建授權中間件: 創建一個 middleware 包,用於處理授權邏輯。
package middleware

import (
   "fmt"

   "github.com/gofiber/fiber/v2"
   "your-project-name/roles"
)

func Authorize(c *fiber.Ctx, permission string) error {
   // 獲取角色
   role := c.Locals("role").(string)

   // 檢查權限
   if !roles.HasPermission(role, permission) {
       return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"message""Unauthorized"})
   }

   return c.Next()
}

// 檢查角色是否擁有權限
func HasPermission(role string, permission string) bool {
   permissions, ok := roles.RolePermissions[role]
   if !ok {
       return false
   }

   for _, p := range permissions {
       if p == permission {
           return true
       }
   }

   return false
}
  1. 使用授權中間件: 在需要授權的路由上使用 Authorize 中間件。
app.Post("/users", middleware.Auth, middleware.Authorize("manage_users"), handler.CreateUser)

錯誤處理

創建一個 error 包,用於處理 API 錯誤。

package error

import (
    "github.com/gofiber/fiber/v2"
)

// 自定義錯誤結構體
type AppError struct {
    Code    int    `json:"code"`
    Status  string `json:"status"`
    Message string `json:"message"`
}

// 創建新的錯誤對象
func New(code int, message string) *AppError {
    return &AppError{
        Code:    code,
        Status:  "error",
        Message: message,
    }
}

// 處理錯誤中間件
func ErrorHandler(c *fiber.Ctx, err error) error {
    // 處理自定義錯誤
    if appErr, ok := err.(*AppError); ok {
        return c.Status(appErr.Code).JSON(appErr)
    }

    // 處理其他錯誤
    return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"status""error""message""Internal server error"})
}

API 文檔

使用 swagger 生成 API 文檔。

  1. 安裝 swag 庫:
go get github.com/swaggo/swag/cmd/swag
  1. 生成 swagger 文件: 在項目根目錄運行 swag init 命令。
swag init
  1. 添加 swagger 註解: 在你的 handler 文件中添加 swagger 註解來描述 API。
// @Summary 創建用戶
// @Description 創建一個新的用戶
// @Tags users
// @Accept  json
// @Produce  json
// @Param   user  body      model.User  true  "用戶信息"
// @Success 201 {object} model.User
// @Failure 400 {object} error.AppError
// @Router /users [post]
func CreateUser(c *fiber.Ctx) error {
   // ... (創建用戶邏輯) ...
}
  1. 啓動 swagger 服務:main.go 文件中添加 swagger 路由。
package main

import (
   "fmt"
   "log"
   "os"

   "github.com/gofiber/fiber/v2"
   "github.com/gofiber/swagger"
   "github.com/joho/godotenv"
   "github.com/jinzhu/gorm"
   _ "github.com/jinzhu/gorm/dialects/postgres"
   "your-project-name/handler"
   "your-project-name/model"
   "your-project-name/error"
   "your-project-name/middleware"
)

func main() {
   // ... (數據庫連接代碼) ...

   // 創建 Fiber 應用
   app := fiber.New()

   // ... (路由定義) ...

   // 註冊錯誤處理中間件
   app.Use(error.ErrorHandler)

   // 註冊 `swagger` 路由
   app.Get("/docs", swagger.HandlerDefault)

   // 啓動服務器
   log.Fatal(app.Listen(fmt.Sprintf(":%s", os.Getenv("APP_PORT"))))
}

測試

  1. 創建測試文件: 創建一個 test 包用於編寫測試用例。
package test

import (
   "testing"

   "github.com/stretchr/testify/assert"
   "your-project-name/handler"
   "your-project-name/model"
)

func TestCreateUser(t *testing.T) {
   // ... (測試用例代碼) ...
}
  1. 編寫測試用例: 在測試文件中編寫測試用例來測試你的 API 功能。
func TestCreateUser(t *testing.T) {
   // 創建測試用戶
   user := &model.User{
       Name:     "Test User",
       Email:    "test@example.com",
       Password: "password",
   }

   // 模擬 API 請求
   c := fiber.New(fiber.Config{
       DisableStartupMessage: true,
   }).Acquire(fiber.AcquireConfig{
       Method: "POST",
       Path:   "/users",
       Body:   user,
   })

   // 調用 API 路由處理器
   err := handler.CreateUser(c)

   // 斷言返回值
   assert.NoError(t, err)
   assert.Equal(t, fiber.StatusCreated, c.Response.StatusCode)

   // 驗證數據庫記錄
   // ... (驗證數據庫記錄) ...
}
  1. 運行測試: 在項目根目錄運行 go test 命令。
go test

其他功能

總結

本文提供了一個完整的 Go RESTful API 開發指南,從項目搭建、數據庫配置到 API 路由、認證、授權、錯誤處理、測試等各個方面進行了詳細講解。希望本文能幫助你快速上手 Go RESTful API 開發,並構建出安全、高效、易維護的 API。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/w1ULznUqQseWivxTf5JA4g