Go Web尝试:基于gin和gorm的Go Web Demo
项目简介
- 目的:这个demo旨在快速了解如何用gin+gorm实现简单的CRUD操作。
- 功能:实现简单的个人信息录入。
- 技术:
- gin:golang的轻量级web框架
- gorm:golang的ORM框架
- 测试工具:postman
- Ps:本demo实现分层,所有的代码都在main.go中。
准备工作
导入gin、gorm、MySQL驱动的包,在命令行运行 go mod tidy
,下载对应包。
import (
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"strconv"
"time"
)
第一步:数据库连接
首先,新建一个dsn(data source name)字符串,具体格式可以按照下面来。
DSN:即数据源名称。通常包含Driver、username、password、address、port、数据库名、其它选项。
调用gorm的Open接口,连接数据库。第一个参数是驱动的dialector,第二个参数是配置,可以在里面配置连接池等。具体请看官方文档。
dsn := "root:123456@tcp(127.0.0.1:3306)/crud?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("fail to connect mysql .")
}
第二步:创建结构体,将结构体迁移到数据库
gorm.Model表示使用gorm定义的默认属性,如ID、deleted_at、created_at等字段。
在结构体标签中配置上数据库的信息、JSON的序列化信息、参数绑定信息。
type List struct {
gorm.Model
Name string `gorm:"type:varchar(20);not null'" json:"name" binding:"required"`
State string `gorm:"type:varchar(20);not null'" json:"state" binding:"required"`
Phone string `gorm:"type:varchar(20);not null'" json:"phone" binding:"required"`
Email string `gorm:"type:varchar(40);not null'" json:"email" binding:"required"`
Address string `gorm:"type:varchar(200);not null'" json:"address" binding:"required"`
}
定义返回的JSON数据格式:
{
"msg": "信息",
"code": 200,//业务状态码 定义200成功,400失败,
"data": //数据
}
//分页
{
"msg": "信息",
"code": 200,//业务状态码 定义200成功,400失败,
"data": {
"list": ,
"total":,
"pageNum":,
"pageSize":,
}
}
AutoMigrate
是 GORM 中的一个功能,用于自动创建或更新数据库表结构,使其与定义的 Go 结构体模型保持同步。这个功能非常方便,可以减少手动管理数据库表结构的工作量。
db.AutoMigrate(&List{})
第三步:创建引擎
engine := gin.Default()
//这里创建添加路由和处理函数
engine.run(":3001") //启动服务,在3001端口监听
gin.default会创建一个默认的 Gin 引擎实例。
Gin 的引擎(Engine)是处理 HTTP 请求和路由的核心组件。gin.Default()
创建的引擎实例具有一些默认的设置,包括:
- 日志输出: 默认情况下,
gin.Default()
会将请求的信息以及响应状态写入标准输出,方便调试和日志记录。 - 恢复中间件: Gin 会自动加载一个恢复中间件,用于在发生 panic 时恢复程序,防止服务器崩溃。这对于保障程序的稳定性很有帮助。
- 请求和响应处理: 引擎会自动处理请求和响应,将请求数据解析为参数、JSON 或者表单数据,并将响应数据编码成 JSON 或者 HTML。
第四步:接口设计——添加路由和处理函数
- engine.GET(),第一个参数接收路由地址,第二个是一个处理函数。
- 处理函数:
- 参数:需要有
*gin.Context
上下文作为参数。 - 返回值:使用C.JSON返回数据。第一个参数是状态码,第二个参数是数据。
- 数据一般用
gin.H{}
结构体封装,本质上是map[string]interface{}
- 参数:需要有
- 如何接收参数:
- 接收JSON数据:
c.ShouldBindJSON(&data)
- RESTful传参:
c.Param("name")
- GET传参:
c.Query("pageSize")
- 接收JSON数据:
- 接口定义:这里接口全部定义为RESTful风格,URL是
/user/data
,根据POST/PUT/GET/DELETE请求,分别执行增加、修改、获取、删除操作。- 新增接口--POST--
/user/data
,传JSON - 查询接口--GET--
/user/data/:name
,条件查询-按照name - 分页查询--GET--
/user/data
,参数:pageSize
、pageNum
- 修改接口--PUT--
/user/data/:id
,传JSON - 删除接口--DELETE--
/user/data/:id
- 注意:gorm默认用的是软删除。
- 新增接口--POST--
第五步:执行SQL语句并返回数据
调用gorm的增删改查接口,进行查询。具体接口看gorm官方文档。
分页查询:
- 公式:
pageOffset := (pageNum - 1) * pageSize
- 查询:首先计算总记录数,然后根据分页信息(每页记录数和页码)来获取相应的数据,从而实现在应用中进行分页显示数据。
db.Model(data).Count(&total).Limit(pageSize).Offset(pageOffset).Find(&data)
处理函数的逻辑:
- 接受参数
- 查询数据库/修改数据
- 返回数据
具体代码如下:
engine.POST("/user/data", func(c *gin.Context) {
var data List
err2 := c.ShouldBindJSON(&data)
if err2 != nil {
fmt.Println("binding error:", err2)
c.JSON(200, gin.H{
"msg": "数据绑定失败",
"data": gin.H{},
"code": 400,
})
} else {
//数据库操作
res := db.Create(&data)
if res.Error != nil {
}
c.JSON(200, gin.H{
"msg": "添加成功",
"data": data,
"code": 200,
})
}
})
//R
//条件查询
engine.GET("/user/data/:name", func(c *gin.Context) {
name := c.Param("name")
var data []List
// 查询数据库
db.Where("name=?", name).Find(&data)
//判断是否查询成功
if len(data) == 0 {
c.JSON(200, gin.H{
"msg": "查询不到数据",
"code": 400,
"data": gin.H{},
})
} else {
c.JSON(200, gin.H{
"msg": "查询成功",
"code": 200,
"data": data,
})
}
})
//分页查询 and 全部查询
engine.GET("/user/data", func(c *gin.Context) {
var data []List
pageSize, _ := strconv.Atoi(c.Query("pageSize"))
pageNum, _ := strconv.Atoi(c.Query("pageNum"))
//判断是否需要分页
if pageSize == 0 {
pageSize = -1
}
if pageNum == 0 {
pageNum = -1
}
pageOffset := (pageNum - 1) * pageSize
//返回总数
var total int64
db.Model(data).Count(&total).Limit(pageSize).Offset(pageOffset).Find(&data)
if len(data) == 0 {
c.JSON(200, gin.H{
"msg": "没有查到数据",
"code": 400,
"data": gin.H{
"list": data,
"total": total,
"pageNum": pageNum,
"pageSize": pageSize,
},
})
} else {
c.JSON(200, gin.H{
"msg": "查询成功",
"code": 200,
"data": gin.H{
"list": data,
"total": total,
"pageNum": pageNum,
"pageSize": pageSize,
},
})
}
})
//U
engine.PUT("/user/data/:id", func(c *gin.Context) {
var data List
id := c.Param("id")
db.Select("id").Where("id=?", id).Find(&data)
if data.ID == 0 {
c.JSON(200, gin.H{
"msg": "用户ID不存在",
"code": 400,
})
} else {
err2 := c.ShouldBindJSON(&data)
if err2 != nil {
c.JSON(200, gin.H{
"msg": "更新失败",
"code": 400,
})
} else {
db.Where("id=?", id).Updates(&data)
c.JSON(200, gin.H{
"msg": "更新成功",
"code": 200,
})
}
}
})
//D
engine.DELETE("/user/data/:id", func(c *gin.Context) {
var data []List
//接收参数,非键值对形式
id := c.Param("id")
c.Query(id)
//查找
db.Where("id=?", id).Find(&data)
if len(data) == 0 {
c.JSON(200, gin.H{
"msg": "删除失败,未找到此用户",
"data": gin.H{},
"code": 400,
})
} else {
db.Where("id=?", id).Delete(&data)
c.JSON(200, gin.H{
"msg": "删除成功",
"code": 200,
})
}
})
结语
到这里,我们就成功用golang开发了简单的支持CRUD的后端服务了。可以看出用Go开发一个服务端非常简单,这也体现了Golang的设计哲学:简单和清晰。不过这份代码有非常多不足的点:如代码不规范,未实现分层和封装等,在更加深入学习go和go框架后,我也会继续更新go web系列文章。
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 AjaxZhan
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果