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 注册全局模板函数
在这个例子中,我们做了以下几件事情:
- 定义了两个控制器函数:
CreateUser
和GetUserByID
,它们分别处理创建新用户和根据 ID 获取用户的请求。 - 解析了 HTTP 请求:使用
c.ShouldBindJSON
方法解析传入的 JSON 数据。 - 调用了服务层:创建了一个
UserService
实例,并调用了它的方法来执行具体的业务逻辑。 - 处理了响应:根据操作的结果返回适当的 HTTP 状态码和响应体。
确保在实际应用中,你已经正确设置了数据库连接,并且在适当的地方初始化了 services.DB
。这可以通过依赖注入或其他方式来实现,以保持代码的整洁和可测试性。
调用 Model 注册全局模板函数
在 Gin 中,如果你想注册全局模板函数以便可以在所有的 HTML 模板中使用这些函数,你可以通过 gin.Engine
的 HTML
渲染器来实现。通常情况下,你会在应用启动时设置这些全局模板函数,这样它们就可以被所有渲染的模板所访问。
下面是一个例子,展示了如何定义和注册全局模板函数,并在控制器中调用 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.Engine
的 HTMLRender
属性。
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.Must
和ParseGlob
来加载所有模板文件,并设置了模板函数。 - 设置了自定义渲染器:将自定义渲染器设置为 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 风格的描述则更简洁:…...

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 配置…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...

376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...