You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Go中基于GoFiber与GORM编写通用CRUD控制器?

解决GoFiber+GORM多实体重复CRUD代码的方案

问题场景

使用GoFiber搭建Go服务器,配合GORM操作MySQL数据库,现有8个业务实体。每个实体都需要实现Create、Update、List、Delete四个控制器方法,还要手动注册对应路由,最终生成32个逻辑重复、仅实体名称不同的方法,代码冗余度极高,急需优化。

实体示例(Account模型)

package models

import "time"

type Account struct {
    ID        uint      `json:"id" gorm:"primary_key;auto_increment;not_null"`
    Name      string    `json:"name"`
    Company   string    `json:"company"`
    GSTIN     string    `json:"gstin"`
    AccountNo string    `json:"accountNo" gorm:"unique"`
    IFSC      string    `json:"ifsc"`
    CreatedAt time.Time `json:"createdAt"`
    UpdatedAt time.Time `json:"updatedAt"`
}

重复控制器示例

package controllers

import (
    "net/http"
    "your-project/models"
    "your-project/database"

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

// GET: List call
func GetAccounts(c *fiber.Ctx) error {
    accounts := new([]models.Account)
    result := database.DB.Find(accounts)
    if result.Error != nil {
        return result.Error
    }
    c.SendStatus(http.StatusOK)
    return c.JSON(accounts)
}

// POST
func CreateAccount(c *fiber.Ctx) error {
    account := new(models.Account)
    err := c.BodyParser(account)
    if err != nil {
        return err
    }
    result := database.DB.Create(account)
    if result.Error != nil{
        return result.Error
    }
    return c.SendStatus(http.StatusCreated)
}

重复路由示例

app.Post("api/account", controllers.CreateAccount)
app.Get("api/accounts", controllers.GetAccounts)

优化方案

1. 抽离基础模型,消除实体重复字段

定义包含所有实体共性字段的BaseModel,让业务实体通过嵌入复用,减少重复代码:

package models

import "time"

type BaseModel struct {
    ID        uint      `json:"id" gorm:"primary_key;auto_increment;not_null"`
    CreatedAt time.Time `json:"createdAt"`
    UpdatedAt time.Time `json:"updatedAt"`
}

// 修改Account实体,嵌入BaseModel
type Account struct {
    BaseModel
    Name      string    `json:"name"`
    Company   string    `json:"company"`
    GSTIN     string    `json:"gstin"`
    AccountNo string    `json:"accountNo" gorm:"unique"`
    IFSC      string    `json:"ifsc"`
}

其余7个实体按同样方式嵌入BaseModel,统一ID与时间戳字段。

2. 用泛型实现通用CRUD控制器

基于Go 1.18+的泛型特性,编写一套适配所有实体的通用CRUD逻辑:

package controllers

import (
    "net/http"
    "your-project/database"

    "github.com/gofiber/fiber/v2"
    "gorm.io/gorm"
)

// GenericController 泛型CRUD控制器
type GenericController[T any] struct{}

// List 通用列表查询
func (gc *GenericController[T]) List(c *fiber.Ctx) error {
    var items []T
    result := database.DB.Find(&items)
    if result.Error != nil {
        return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()})
    }
    return c.Status(http.StatusOK).JSON(items)
}

// Create 通用创建
func (gc *GenericController[T]) Create(c *fiber.Ctx) error {
    item := new(T)
    if err := c.BodyParser(item); err != nil {
        return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
    }
    result := database.DB.Create(item)
    if result.Error != nil {
        return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()})
    }
    return c.Status(http.StatusCreated).JSON(item)
}

// Update 通用更新(按ID)
func (gc *GenericController[T]) Update(c *fiber.Ctx) error {
    id := c.Params("id")
    item := new(T)

    // 校验记录是否存在
    if err := database.DB.First(item, id).Error; err != nil {
        if err == gorm.ErrRecordNotFound {
            return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": "record not found"})
        }
        return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
    }

    // 解析请求体更新字段
    if err := c.BodyParser(item); err != nil {
        return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
    }

    result := database.DB.Save(item)
    if result.Error != nil {
        return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()})
    }
    return c.Status(http.StatusOK).JSON(item)
}

// Delete 通用删除(按ID)
func (gc *GenericController[T]) Delete(c *fiber.Ctx) error {
    id := c.Params("id")
    item := new(T)

    if err := database.DB.Delete(item, id).Error; err != nil {
        return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
    }
    return c.SendStatus(http.StatusNoContent)
}

3. 批量注册通用路由

创建路由注册工具,通过实体映射批量生成CRUD路由,避免手动编写:

package routes

import (
    "your-project/controllers"
    "your-project/models"

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

// EntityRoute 实体路由配置
type EntityRoute struct {
    SingleName string // 单个实体名称,如"account"
    PluralName string // 复数名称,如"accounts"
}

// RegisterGenericRoutes 注册所有实体的CRUD路由
func RegisterGenericRoutes(app *fiber.App) {
    entities := []EntityRoute{
        {SingleName: "account", PluralName: "accounts"},
        {SingleName: "user", PluralName: "users"},
        // 依次添加其余6个实体
    }

    // 为每个实体实例化泛型控制器并注册路由
    for _, ent := range entities {
        switch ent.SingleName {
        case "account":
            ctrl := new(controllers.GenericController[models.Account])
            registerCRUDRoutes(app, ent, ctrl)
        case "user":
            ctrl := new(controllers.GenericController[models.User])
            registerCRUDRoutes(app, ent, ctrl)
        // 其余实体同理添加case
        }
    }
}

// registerCRUDRoutes 注册单个实体的CRUD路由
func registerCRUDRoutes[T any](app *fiber.App, ent EntityRoute, ctrl *controllers.GenericController[T]) {
    basePath := "/api/" + ent.SingleName
    pluralPath := "/api/" + ent.PluralName

    app.Post(basePath, ctrl.Create)
    app.Get(pluralPath, ctrl.List)
    app.Put(basePath+"/:id", ctrl.Update)
    app.Delete(basePath+"/:id", ctrl.Delete)
}

在main函数中调用路由注册:

func main() {
    app := fiber.New()
    // 初始化数据库连接...
    routes.RegisterGenericRoutes(app)
    app.Listen(":3000")
}

额外优化建议

  • 在通用List方法中添加分页、过滤逻辑,解析query参数实现分页:page := c.QueryInt("page", 1); limit := c.QueryInt("limit", 10); database.DB.Limit(limit).Offset((page-1)*limit).Find(&items)
  • 若特定实体需要自定义逻辑(如Account的Create需校验GSTIN),可单独编写该方法并替换路由中对应的控制器方法。

内容的提问来源于stack exchange,提问作者Anmol Garg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 13:42:56