当前位置: 首页 > news >正文

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

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

自定义 Model

定义结构体

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

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

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

配置数据库连接

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

package mainimport ("gorm.io/driver/sqlite""gorm.io/gorm""log"
)var db *gorm.DB
var err errorfunc 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 modelsimport ("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 servicesimport ("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 modelsimport ("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 mainimport ("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 controllersimport ("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.Userif err := c.ShouldBindJSON(&newUser); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}userService := services.NewUserService(services.DB) // 假设 DB 已经被初始化并赋值给 services.DBif 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.DBuser, 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 mainimport ("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 mainimport ("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 已经被正确初始化。此外,根据你的项目结构和需求调整包名和导入路径。

相关文章:

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

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

【VBA】EXCEL - VBA 创建 Sheet 表的 6 种方法,以及注意事项

目录 1. 创建一个新工作表,并将其添加到工作簿的末尾 2. 创建一个新工作表,并命名它 3. 创建一个新工作表,并将其插入到指定位置 4. 检查是否已有同名工作表,避免重复创建 5. 创建多个工作表 6. 基于现有模板创建新工作表 …...

数据库中,group by 和partition by:数据分组和数据分区的区别

数据库中,group by 和partition by:数据分组和数据分区的区别 在大规模数据处理和分析的场景中,对数据进行分区和分组处理是非常常见的场景。 为了实现这一操作,在一些主流的关系型数据库管理系统中,提供了group by 和…...

【linux学习指南】Ext系列文件系统(四)路径分区链接

文章目录 🌠⽬录与⽂件名🌠路径解析🌠路径缓存🌠挂载分区🌉 ⽂件系统总结 🌠软硬连接🌉 硬链接🌉 软链接🌉 软硬连接对⽐🌉软硬连接的⽤途: &…...

深度学习中的参数初始化

深度学习中的参数初始化主要是指初始化神经网络中的权重和偏置。权重和偏置通常分开初始化,偏置通常初始化为零或较小的常数值。 没有一种万能的初始化技术,因为最佳初始化可能因具体架构和要解决的问题而异。因此,尝试不同的初始化技术以了解…...

wpf 基于Behavior库 的行为模块

Microsoft.Xaml.Behaviors 是一个用于WPF(Windows Presentation Foundation)的行为库,它的主要作用是允许开发者在不修改控件源代码的情况下,为控件添加自定义的行为和交互逻辑。行为库的核心思想是通过定义可重用的行为组件&…...

【每日学点鸿蒙知识】导入cardEmulation、自定义装饰器、CallState状态码顺序、kv配置、签名文件配置

1、HarmonyOS 无法导入cardEmulation? 在工程entry mudule里的index.ets文件里导入cardEmulation失败 可以按照下面方式添加SystemCapability;在src/main/syscap.json(此文件需要手动创建)中添加如下内容 {"devices": {"gen…...

【SpringMVC】REST 风格

REST(Representational State Transfer,表现形式状态转换)是一种访问网络资源的格式。传统的资源描述方式通常如下: http://localhost/user/getById?id1http://localhost/user/saveUser 而 REST 风格的描述则更简洁&#xff1a…...

IDEA修改编译版本

目录 一、序言 二、修改maven配置 1.修改 2.代码 三、pom文件配置 1.修改 2.代码 3.问题 一、序言 有两种方法可以帮助大家解决IDEA每次刷新maven的pom配置时,会发生发行源版本不正常的报错。个人推荐第二种,原因:第二种你刷新maven后…...

SkyWalking Agent 配置 Spring Cloud Gateway 插件解决日志错误

SkyWalking Agent 配置 Spring Cloud Gateway 插件解决日志错误 IDEA中启动网管时,需要配置VM启动参数,格式如下: # 配置 SkyWalking Agent 启动参数,以便将网关服务的性能数据上报到 SkyWalking 服务器。 -javaagent:/path/to/sk…...

canvas+fabric实现时间刻度尺(一)

前言 需求:显示一个时间刻度尺,鼠标移动会显示当前时间 技术:我们采用canvasfabric进行实现 效果 实现 1.创建canvas(设置宽高)设为全局变量 2.引入fabric包 3.画时间刻度尺(长方形横线) …...

傲雷亮相2024中国时尚体育季(珠海站),展现户外移动照明风采

2024年12月28-29日,2024中国时尚体育季(珠海站)国家级轮滑比赛在珠海金山体育公园成功举办。作为户外创新型移动照明领域的领导品牌,傲雷受邀参加了本次珠海金湾运动生活嘉年华的展览单元,与众多户外运动品牌同台展示。…...

YOLOv10-1.1部分代码阅读笔记-block.py

block.py ultralytics\nn\modules\block.py 目录 block.py 1.所需的库和模块 2.class DFL(nn.Module): 3.class Proto(nn.Module): 4.class HGStem(nn.Module): 5.class HGBlock(nn.Module): 6.class SPP(nn.Module): 7.class SPPF(nn.Module): 8.class C1(nn…...

@RestControllerAdvice注解

RestControllerAdvice 是 Spring 4 引入的一个组合注解,它结合了 ControllerAdvice 和 ResponseBody,专门用于处理 RestController 类型的控制器中的全局异常、全局数据绑定和全局模型属性等问题。在 Spring Boot 中,RestControllerAdvice 通…...

Enum枚举类与静态变量和静态数组的区别

Enum枚举类与静态变量和静态数组的区别 组成结构Enum枚举类静态变量静态数组 组成结构的区别相同之处不同之处 用法使用相同之处不同之处 组成结构 先来看下Enum枚举类,静态变量,静态数组的初始化过程,以下面为例子: public enu…...

uniapp——微信小程序读取bin文件,解析文件的数据内容(三)

微信小程序读取bin文件内容 读取用户选择bin文件,并解析数据内容,分包发送给蓝牙设备; 文章目录 微信小程序读取bin文件内容读取文件读取内容返回格式 API文档: getFileSystemManager 关于App端读取bin文件,请查看&…...

SpringBoot集成ECDH密钥交换

简介 对称加解密算法都需要一把秘钥,但是很多情况下,互联网环境不适合传输这把对称密码,有密钥泄露的风险,为了解决这个问题ECDH密钥交换应运而生 EC:Elliptic Curve——椭圆曲线,生成密钥的方法 DH&…...

python文件操作相关(excel)

python文件操作相关(excel) 1. openpyxl 库openpyxl其他用法创建与删除操作单元格追加数据格式化单元格合并单元格插入图片公式打印设置保护工作表其他功能 2. pandas 库3. xlrd 和 xlwt 库4. xlsxwriter 库5. pyxlsb 库应用场景参考资料 在 Python 中&a…...

探索React与Microi吾码的完美结合:快速搭建项目,低代码便捷开发教程

一、摘要 在当今的数字化时代,软件开发就像是一场探险,每个开发者都是探险家,探索着代码的奥秘。React作为前端开发的领军框架,其组件化和高效的渲染机制为开发者提供了强大的工具。而Microi吾码低代码平台的出现,则为…...

【面试系列】深入浅出 Spring Boot

熟悉SpringBoot,对常用注解、自动装配原理、Jar启动流程、自定义Starter有一定的理解; 面试题 Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?Spring Boot的自动配置原理是什么?你如何理解 Spring Boot 配置…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...