9-Gin 中自定义 Model --[Gin 框架入门精讲与实战案例]

news2025/1/3 16:10:02

在 Gin 框架中自定义 Model 通常指的是定义你自己的数据结构,这些结构体(Structs)将用来表示数据库中的表、API 请求的参数或响应的数据格式。下面是如何在 Gin 中创建和使用自定义 Model 的基本步骤。

自定义 Model

定义结构体

首先,你需要定义一个或多个 Go 结构体来表示你的数据模型。例如:

package models

type User struct {
    ID        uint   `json:"id" gorm:"primaryKey"`
    Name      string `json:"name" binding:"required"`
    Email     string `json:"email" binding:"required,email"`
    CreatedAt time.Time
    UpdatedAt time.Time
}

在这个例子中,User 结构体包含了用户的基本信息,并且每个字段都有 JSON 标签用于 API 响应时的序列化,以及 GORM 标签用于数据库操作。binding标签是用于验证请求数据的。

配置数据库连接

如果你打算将这些模型与数据库一起使用,你需要配置数据库连接。Gin 本身不处理数据库操作,但常常与 GORM 等 ORM 库一起使用。以下是一个简单的例子,说明如何设置 GORM 数据库连接:

package main

import (
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "log"
)

var db *gorm.DB
var err error

func init() {
    // 连接到 SQLite 数据库 (这里可以替换为其他数据库)
    db, err = gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
    if err != nil {
        log.Fatal(err)
    }

    // 自动迁移模式,根据模型自动创建表
    db.AutoMigrate(&models.User{})
}

使用模型进行 CRUD 操作

接下来,你可以编写函数来进行创建、读取、更新和删除(CRUD)操作。例如,创建一个新的用户记录:

func CreateUser(user *models.User) (*models.User, error) {
    result := db.Create(user)
    if result.Error != nil {
        return nil, result.Error
    }
    return user, nil
}

将模型用于 HTTP 请求

最后,你可以将这些模型与 Gin 路由器结合使用,以处理来自客户端的 HTTP 请求。例如:

func RegisterUser(c *gin.Context) {
    var user models.User
    // 绑定和验证请求数据
    if err := c.ShouldBindJSON(&user); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    // 创建新用户
    newUser, err := CreateUser(&user)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "无法创建用户"})
        return
    }

    // 返回创建的用户信息
    c.JSON(http.StatusOK, newUser)
}

以上就是如何在 Gin 中自定义 Model 的简要介绍。当然,实际项目可能会更复杂,涉及到更多的业务逻辑、错误处理等。

Model 里面封装公共的方法

在 Gin 中,如果你希望为 Model 封装公共的方法,可以通过定义方法或使用 Go 的组合特性来实现。这里有几个常见的模式可以用来封装模型的公共方法:

方法1:直接在结构体上定义方法

你可以直接在你的模型结构体上定义方法,这些方法可以直接访问和操作结构体的字段。例如:

package models

import (
    "gorm.io/gorm"
    "time" // 引入 time 包以使用时间类型
)

// User 代表系统中的用户实体。
// 它包含了用户的 ID、名称、电子邮件地址以及创建和更新的时间戳。
type User struct {
    ID        uint      `json:"id" gorm:"primaryKey"` // 用户的唯一标识符(主键)
    Name      string    `json:"name" binding:"required"` // 用户的名字,是必填字段
    Email     string    `json:"email" binding:"required,email"` // 用户的电子邮件地址,必须是有效的电子邮件格式且为必填
    CreatedAt time.Time // 用户记录创建的时间戳
    UpdatedAt time.Time // 用户记录最后更新的时间戳
}

// Save 保存当前用户实例到数据库。
// 如果用户已存在,则更新现有记录;如果不存在,则插入新记录。
// 参数:
//   - db: GORM 数据库连接实例
// 返回值:
//   - error: 如果操作失败则返回错误信息,否则返回 nil 表示成功
func (u *User) Save(db *gorm.DB) error {
    return db.Save(u).Error // 使用 GORM 的 Save 方法来持久化用户数据,并检查是否有错误发生
}

// Delete 删除当前用户实例。
// 参数:
//   - db: GORM 数据库连接实例
// 返回值:
//   - error: 如果操作失败则返回错误信息,否则返回 nil 表示成功
func (u *User) Delete(db *gorm.DB) error {
    return db.Delete(u).Error // 使用 GORM 的 Delete 方法来移除用户数据,并检查是否有错误发生
}

方法2:使用服务层

另一种方式是创建一个服务层(Service Layer),其中包含与特定模型相关的业务逻辑。这可以帮助你保持代码的整洁,并且更易于测试。

package services

import (
    "your_project/models"
    "gorm.io/gorm"
)

// UserService 提供了对用户模型的一系列操作方法。
// 它依赖于 GORM 数据库连接实例来进行数据库交互。
type UserService struct {
    DB *gorm.DB // 数据库连接实例,用于执行所有数据库操作
}

// NewUserService 创建一个新的 UserService 实例。
// 参数:
//   - db: GORM 数据库连接实例
// 返回值:
//   - *UserService: 返回一个初始化好的 UserService 实例
func NewUserService(db *gorm.DB) *UserService {
    return &UserService{DB: db}
}

// CreateUser 在数据库中创建一个新的用户记录。
// 参数:
//   - user: 指向 models.User 的指针,包含了要保存到数据库的新用户的详情
// 返回值:
//   - error: 如果创建过程中出现问题,则返回错误信息;否则返回 nil 表示成功
func (us *UserService) CreateUser(user *models.User) error {
    return us.DB.Create(user).Error // 使用 GORM 的 Create 方法来插入新的用户数据,并检查是否有错误发生
}

// GetUserByID 根据提供的 ID 获取用户信息。
// 参数:
//   - id: 用户的唯一标识符(主键)
// 返回值:
//   - *models.User: 包含查询结果的 User 结构体指针,如果未找到则为 nil
//   - error: 如果查询过程中出现问题,则返回错误信息;否则返回 nil 表示成功
func (us *UserService) GetUserByID(id uint) (*models.User, error) {
    var user models.User
    // 使用 GORM 的 First 方法根据主键查找用户,如果找不到或发生错误则返回相应的错误
    if err := us.DB.First(&user, id).Error; err != nil {
        return nil, err
    }
    return &user, nil // 成功找到用户时,返回该用户的指针
}

方法3:使用接口和组合

如果你想使你的模型更加灵活,你可以定义接口并在其他类型中实现这些接口,或者通过组合来共享行为。这种方法对于需要跨多个模型共享相同行为的情况特别有用。

package models

import (
    "gorm.io/gorm"
)

// Entity 定义了一个接口,表示所有实体应该具有的基本方法。
// 这个接口可以被任何需要共享 ID 行为的模型实现。
type Entity interface {
    // GetID 返回实体的唯一标识符(主键)。
    GetID() uint
    // SetID 设置实体的唯一标识符(主键)。
    SetID(uint)
}

// BaseEntity 是一个基础结构体,包含了所有实体共有的字段和方法。
// 它实现了 Entity 接口,并提供了一个默认的 ID 字段。
type BaseEntity struct {
    ID uint `json:"id" gorm:"primaryKey"` // 实体的唯一标识符(主键),用于数据库中的记录识别
}

// GetID 返回当前实体的 ID 值。
func (b *BaseEntity) GetID() uint {
    return b.ID
}

// SetID 设置当前实体的 ID 值。
func (b *BaseEntity) SetID(id uint) {
    b.ID = id
}

// User 继承了 BaseEntity 结构体,因此它自动获得了 ID 字段及其方法。
// 此外,User 结构体还包含额外的字段,如 Name 和 Email,
// 用于存储用户的具体信息。
type User struct {
    BaseEntity // 匿名字段,使得 User 拥有 BaseEntity 的所有字段和方法
    Name      string `json:"name" binding:"required"`     // 用户的名字,是必填字段
    Email     string `json:"email" binding:"required,email"` // 用户的电子邮件地址,必须是有效的电子邮件格式且为必填
}

在这个例子中,BaseEntity 包含了所有实体可能共有的字段和方法,而 User 继承了这些字段和方法。

选择哪种方式取决于你的项目需求和个人偏好。通常来说,将业务逻辑放在服务层是一个不错的选择,因为它使得代码更模块化、可维护和可测试。同时,直接在模型上定义方法也可以简化一些基本的操作。

控制器中调用 Model

在 Gin 框架中,控制器(Controller)是处理 HTTP 请求和响应的地方。通常情况下,控制器会调用 Model 来执行业务逻辑或与数据库进行交互。下面是一个完整的例子,展示了如何在控制器中调用 Model。

假设我们已经有了 User 模型和一个 UserService 服务层,现在我们要创建一个控制器来处理用户的创建和获取请求。

定义路由和控制器

首先,在你的主程序文件(如 main.go)中设置 Gin 路由,并将这些路由映射到控制器方法:

package main

import (
    "your_project/controllers"
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()

    // 定义用户相关路由
    userRoutes := r.Group("/users")
    {
        userRoutes.POST("", controllers.CreateUser)
        userRoutes.GET("/:id", controllers.GetUserByID)
    }

    // 启动服务器
    r.Run(":8080")
}

创建控制器

接下来,在 controllers 包中创建控制器函数,这些函数将调用 UserService 中的方法来处理业务逻辑:

package controllers

import (
    "net/http"
    "your_project/models"
    "your_project/services"
    "github.com/gin-gonic/gin"
)

// CreateUser 控制器用于处理创建新用户的 POST 请求。
// 它解析请求体中的 JSON 数据,调用 UserService 来创建用户,
// 并返回新创建的用户信息或错误。
func CreateUser(c *gin.Context) {
    var newUser models.User
    if err := c.ShouldBindJSON(&newUser); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    userService := services.NewUserService(services.DB) // 假设 DB 已经被初始化并赋值给 services.DB
    if err := userService.CreateUser(&newUser); err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "无法创建用户"})
        return
    }

    c.JSON(http.StatusCreated, newUser)
}

// GetUserByID 控制器用于处理通过 ID 获取用户的 GET 请求。
// 它从 URL 参数中提取用户 ID,调用 UserService 来获取用户,
// 并返回用户信息或错误。
func GetUserByID(c *gin.Context) {
    id := c.Param("id")
    userId, err := strconv.ParseUint(id, 10, 64)
    if err != nil || userId == 0 {
        c.JSON(http.StatusBadRequest, gin.H{"error": "无效的用户 ID"})
        return
    }

    userService := services.NewUserService(services.DB) // 假设 DB 已经被初始化并赋值给 services.DB
    user, err := userService.GetUserByID(uint(userId))
    if err != nil {
        c.JSON(http.StatusNotFound, gin.H{"error": "用户未找到"})
        return
    }

    c.JSON(http.StatusOK, user)
}

调用 Model 注册全局模板函数

在这个例子中,我们做了以下几件事情:

  • 定义了两个控制器函数CreateUserGetUserByID,它们分别处理创建新用户和根据 ID 获取用户的请求。
  • 解析了 HTTP 请求:使用 c.ShouldBindJSON 方法解析传入的 JSON 数据。
  • 调用了服务层:创建了一个 UserService 实例,并调用了它的方法来执行具体的业务逻辑。
  • 处理了响应:根据操作的结果返回适当的 HTTP 状态码和响应体。

确保在实际应用中,你已经正确设置了数据库连接,并且在适当的地方初始化了 services.DB。这可以通过依赖注入或其他方式来实现,以保持代码的整洁和可测试性。

调用 Model 注册全局模板函数

在 Gin 中,如果你想注册全局模板函数以便可以在所有的 HTML 模板中使用这些函数,你可以通过 gin.EngineHTML 渲染器来实现。通常情况下,你会在应用启动时设置这些全局模板函数,这样它们就可以被所有渲染的模板所访问。

下面是一个例子,展示了如何定义和注册全局模板函数,并在控制器中调用 Model 来传递数据给模板:

1. 定义全局模板函数

首先,在你的主程序文件(如 main.go)中设置全局模板函数:

package main

import (
    "html/template"
    "net/http"
    "your_project/models"
    "github.com/gin-gonic/gin"
)

func init() {
    // 注册全局模板函数
    gin.DefaultRenderer().(*renderer.Renderer).Funcs(template.FuncMap{
        "formatDate": func(t time.Time) string {
            return t.Format("2006-01-02")
        },
        "getUserByID": func(id uint) *models.User {
            // 这里应该有一个适当的数据库连接和服务层来获取用户信息
            userService := services.NewUserService(services.DB)
            user, _ := userService.GetUserByID(id)
            return user
        },
        // 可以添加更多的模板函数...
    })
}

请注意,gin.DefaultRenderer() 可能不是最新的 API 调用方式;具体取决于你使用的 Gin 版本。对于较新的版本,你可能需要直接操作 gin.EngineHTMLRender 属性。

2. 使用自定义渲染器(推荐)

为了确保兼容性和更好的控制,推荐创建一个自定义的渲染器实例并将其配置为 Gin 的默认渲染器。这可以让你更灵活地管理模板路径、布局等。

package main

import (
    "html/template"
    "net/http"
    "your_project/models"
    "github.com/gin-gonic/gin"
    "github.com/gin-gonic/gin/render"
)

func main() {
    r := gin.Default()

    // 创建一个新的自定义渲染器实例
    renderer := render.HTML{
        Templates: template.Must(template.New("").Funcs(template.FuncMap{
            "formatDate": func(t time.Time) string {
                return t.Format("2006-01-02")
            },
            "getUserByID": func(id uint) *models.User {
                userService := services.NewUserService(services.DB)
                user, _ := userService.GetUserByID(id)
                return user
            },
            // 可以添加更多的模板函数...
        }).ParseGlob("templates/*.tmpl")),
    }

    // 设置自定义渲染器为默认渲染器
    r.HTMLRender = renderer

    // 定义路由和控制器逻辑...
    r.GET("/user/:id", func(c *gin.Context) {
        id := c.Param("id")
        userId, err := strconv.ParseUint(id, 10, 64)
        if err != nil || userId == 0 {
            c.JSON(http.StatusBadRequest, gin.H{"error": "无效的用户 ID"})
            return
        }

        c.HTML(http.StatusOK, "user.tmpl", gin.H{
            "userID": userId,
        })
    })

    // 启动服务器
    r.Run(":8080")
}

在这个例子中,我们做了以下几件事情:

  • 定义了全局模板函数:包括格式化日期和根据 ID 获取用户的函数。
  • 创建了自定义渲染器:使用 template.MustParseGlob 来加载所有模板文件,并设置了模板函数。
  • 设置了自定义渲染器:将自定义渲染器设置为 Gin 的默认 HTML 渲染器。
  • 定义了一个路由:该路由处理 /user/:id 请求,并调用了 c.HTML 方法来渲染模板,同时传递了必要的数据。

请确保替换 "templates/*.tmpl" 为实际模板文件的路径模式,并且确保 services.DB 已经被正确初始化。此外,根据你的项目结构和需求调整包名和导入路径。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2269351.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IDEA工具使用介绍、IDEA常用设置以及如何集成Git版本控制工具

文章目录 一、IDEA二、建立第一个 Java 程序三、IDEA 常用设置四、IDEA 集成版本控制工具(Git、GitHub)4.1 IDEA 拉 GitHub/Git 项目4.2 IDEA 上传 项目到 Git4.3 更新提交命令 一、IDEA 1、什么是IDEA? IDEA,全称为 IntelliJ ID…

STM32 I2C通信协议

单片机学习! 文章目录 目录 文章目录 前言 一、I2C通信 1.1 I2C总线 1.2 I2C通信线 1.3 同步半双工且数据应答 1.4 一主多从 二、硬件电路 2.1 I2C电路模型 2.2 I2C接线要求 2.3 I2C上拉电阻作用 三、I2C时序基本单元 3.1 起始终止条件 3.1.1 起始条件 3.1.2 终止条…

在线免费批量生成 Word 文档工具

为了方便的批量生成 Word 文档,写了个在线 Word 文档批量生成工具,可以根据 Excel 数据和 Word 模板批量生成大量个性化的 Word 文档。适用于需要批量生成格式统一但内容不同的文档场景。比如: 批量生成证书、奖状批量生成合同、协议批量生成…

再见了我的2024

目录 户外运动 阅读及影视剧欣赏 提升专业技能 外婆走了 消费分析 小妹家的二宝 2024 summary 2025年几个小目标 2024生活记录 2024年最后一天,就这样过去了。 总是来不及好好地告个别,就像我们的年老的亲人一样,见一面,少…

Java工程师实现视频文件上传minio文件系统存储及网页实现分批加载视频播放

Java工程师实现minio存储大型视频文件网页实现分批加载视频播放 一、需求说明 老板给我出个题目,让我把的电影文件上传到minio文件系统,再通过WEB端分配加载视频播放,类似于我们普通的电影网站。小编把Java代码共享出来。是真正的能拿过来直…

Three.js教程004:坐标辅助器与轨道控制器

文章目录 坐标辅助器与轨道控制器实现效果添加坐标辅助器添加轨道控制器完整代码完整代码下载坐标辅助器与轨道控制器 实现效果 添加坐标辅助器 创建坐标辅助器: const axesHelper = new Three.AxesHelper(5);添加到场景中: scene.

【优选算法 分治】深入理解分治算法:分治算法入门小专题详解

快速排序算法 (1) 快速排序法 (2) 快排前后指针 (3) 快排挖坑法 颜色分类 题目解析 算法原理 算法原理和移动零非常相似 简述移动零的算法原理 cur 在从前往后扫描的过程中,如果扫描的数符合 f 性质,就把这个数放到 dest 之…

Qt5 中 QGroupBox 标题下沉问题解决

我们设置了QGroupBox 样式之后,发现标题下沉了,那么如何解决呢? QGroupBox {font: 12pt "微软雅黑";color:white;border:1px solid white;border-radius:6px; } 解决后的效果 下面是解决方法: QGroupBox {font: 12pt "微软雅黑";color:white;bo…

sentinel-请求限流、线程隔离、本地回调、熔断

请求限流:控制QPS来达到限流的目的 线程隔离:控制线程数量来达到限流的目录 本地回调:当线程被限流、隔离、熔断之后、就不会发起远程调用、而是使用本地已经准备好的回调去提醒用户 服务熔断:熔断也叫断路器,当失败、…

体验Cursor一段时间后的感受和技巧

用这种LLM辅助的IDE一段时间了,断断续续做了几个小项目了,总结一下整体的感受和自己的一些使用经验。 从Cursor开始又回到Cursor 第一个真正开始使用的LLM的辅助开发IDE就是Cursor,Github的Copilot支持尝试过,但是并没有真正的在…

【数据仓库】hadoop3.3.6 安装配置

文章目录 概述下载解压安装伪分布式模式配置hdfs配置hadoop-env.shssh免密登录模式设置初始化HDFS启动hdfs配置yarn启动yarn 概述 该文档是基于hadoop3.2.2版本升级到hadoop3.3.6版本,所以有些配置,是可以不用做的,下面仅记录新增操作&#…

宽带、光猫、路由器、WiFi、光纤之间的关系

1、宽带(Broadband) 1.1 宽带的定义宽带指的是一种高速互联网接入技术,通常包括ADSL、光纤、4G/5G等不同类型的接入方式。宽带的关键特点是能够提供较高的数据传输速率,使得用户可以享受到稳定的上网体验。 1.2 宽带的作用宽带是…

[2025] 如何在 Windows 计算机上轻松越狱 IOS 设备

笔记 1. 首次启动越狱工具时,会提示您安装驱动程序。单击“是”确认安装,然后再次运行越狱工具。 2. 对于Apple 6s-7P和iPad系列(iOS14.4及以上),您应该点击“Optinos”并勾选“允许未经测试的iOS/iPadOS/tvOS版本”&…

Linux SVN下载安装配置客户端

参考: linux下svn服务器搭建及使用(包含图解)_小乌龟svn新建用户名和密码-CSDN博客 1.ubuntu安装svn客户端 “subversion” sudo apt-get update sudo apt-get install subversion 查看安装的版本信息,同时看是否安装成功 s…

【Windows】Windows系统查看目录中子目录占用空间大小

在对应目录下通过powershell命令查看文件夹及文件大小,不需要管理员权限。 以下为方式汇总: 方式1(推荐,免费下载使用,界面友好): 使用工具以下是一些第三方工具treesize_free https://www.ja…

【论文阅读笔记】IceNet算法与代码 | 低照度图像增强 | IEEE | 2021.12.25

目录 1 导言 2 相关工作 A 传统方法 B 基于CNN的方法 C 交互方式 3 算法 A 交互对比度增强 1)Gamma estimation 2)颜色恢复 3)个性化初始η B 损失函数 1)交互式亮度控制损失 2)熵损失 3)平滑损失 4)总损失 C 实现细节 4 实验 5 IceNet环境配置和运行 1 下载…

L25.【LeetCode笔记】 三步问题的四种解法(含矩阵精彩解法!)

目录 1.题目 2.三种常规解法 方法1:递归做 ​编辑 方法2:改用循环做 初写的代码 提交结果 分析 修改后的代码 提交结果 for循环的其他写法 提交结果 方法3:循环数组 提交结果 3.方法4:矩阵 算法 代码实践 1.先计算矩阵n次方 2.后将矩阵n次方嵌入递推式中 提…

小白投资理财 - 看懂 PE Ratio 市盈率

小白投资理财 - 看懂 PE Ratio 市盈率 什么是 PE RatioPE 缺陷PE 优点总结 无论是在菜市还是股票市场,每个人都想捡便宜,而买股票就像市场买菜,必须货比三家,投资股票最重要就是要知道回本时间要多久,市场上很多时候散…

python利用selenium实现大麦网抢票

大麦网(damai.cn)是中国领先的现场娱乐票务平台,涵盖演唱会、音乐会、话剧、歌剧、体育赛事等多种门票销售。由于其平台上经常会有热门演出,抢票成为许多用户关注的焦点。然而,由于票务资源的有限性,以及大…

SQL-leetcode-183. 从不订购的客户

183. 从不订购的客户 Customers 表: -------------------- | Column Name | Type | -------------------- | id | int | | name | varchar | -------------------- 在 SQL 中,id 是该表的主键。 该表的每一行都表示客户的 ID 和名称。 Orders 表&#…