简介
只作为快速入门、了解Go的Gin+Gorm框架的demo,不能作为企业级开发。
详细用法请看官网
《Gin官网》
《Gorm官网》
使用GoLand创建Go项目(默认modules)
go版本1.22.2
需要设置代理下载go相关软件包,否则软件包可能无法下载。
https://goproxy.cn
目录结构
go.mod就是管理相关软件的版本信息,如果你学习过Java,可以简单理解mod就是maven、gradle等构建工具
安装 Gin + Gorm 软件包
控制台执行以下三条命令,否则启动会报错。
gin web框架
gorm orm框架
gorm mysql 数据库驱动
go get -u github.com/gin-gonic/gin
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
创建mysql数据库
创建main.go文件
go有且仅有一个main函数,这个是跟java不太一样的地方,java是允许有多个main函数的。
//包的概念
package main
//引入的包路径
import (
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"net/http"
"path"
"time"
)
//定义一个结构体
type UserInfo struct {
gorm.Model //gorm模型默认包含 ID、CreatedAt、UpdatedAt、DeletedAt 4个变量
Username string `gorm:"index" form:"username" json:"username" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
}
// 自定义 gin 中间件 MyHandlerFunc
func MyHandlerFunc(invoke bool) gin.HandlerFunc {
return func(c *gin.Context) {
if invoke {
fmt.Println("请求开始......")
start := time.Now()
//在请求上下文中设置值,后续的处理函数可以获取到该值
c.Set("token", "1")
token, ok := c.Get("token")
if !ok {
c.JSON(http.StatusUnauthorized, gin.H{
"code": http.StatusUnauthorized,
"message": "未授权",
})
return
}
fmt.Printf("token = %v\n", token)
c.Next()
//c.Abort()表示阻止代码继续往下执行
//c.Abort()
//计算请求耗时
cost := time.Since(start)
fmt.Println("请求耗时:", cost)
fmt.Println("请求结束......")
} else {
c.Next()
}
}
}
//main函数
func main() {
//0.在控制台输入 go get -u github.com/gin-gonic/gin 安装 Gin 软件包
//返回默认路由引擎
r := gin.Default()
//指定用户使用GET请求访问/hello,并执行匿名函数,返回JSON格式map[string]的数据
//1.返回map(一般用于临时返回,不推荐)
r.GET("/map", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "Hello World",
})
})
/*
2.返回结构体【推荐】(类似java中的对象,但是首字母必须大写,否则无法进行序列化和反序列化)
以及灵活使用tag来对结构体进行定制化操作
*/
type Person struct {
//后面写`json:"name"`表示如果以json返回,则变量名使用name
Name string `json:"name"`
Message string `json:"message"`
Age int `json:"age"`
}
r.GET("/struct", func(c *gin.Context) {
data := Person{"Meta39", "Java 学习 Go", 25}
c.JSON(http.StatusOK, data)
})
//3.请求地址携带参数的请求【不推荐】(主要使用c.Query,访问:localhost:8080/query?name=Meta39&age=25,浏览器返回Meta39,控制台输出age: 25)
r.GET("/query", func(c *gin.Context) {
name := c.Query("name")
age := c.Query("age")
c.JSON(http.StatusOK, gin.H{
"name": name,
})
fmt.Printf("age: %s\n", age)
})
//4.获取URL路径参数【推荐】(根据用户ID查询用户信息,访问:localhost:8080/user/1)
//relativePath也可以是/user/:id/:age,多个组合
r.GET("/user/:id", func(c *gin.Context) {
id := c.Param("id")
c.JSON(http.StatusOK, gin.H{
"id": id,
"name": "Meta39",
"age": 25,
"sex": "Man",
})
})
//5.参数绑定ShouldBind【推荐】(访问:localhost:8080/shouldBind,在body {"username": "mata","password": "123456"})
r.POST("/shouldBind", func(c *gin.Context) {
//声明UserInfo类型的变量u
var u UserInfo
//& 类似于 Spring @RequestBody 注解
err := c.ShouldBind(&u)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
fmt.Printf("%+v\n", u)
c.JSON(http.StatusOK, gin.H{
"success": "ok",
})
})
//6.文件上传
r.POST("/upload", func(c *gin.Context) {
//从请求中读取文件
f, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
//将读取的文件保存到本地
dst := path.Join("./", f.Filename)
_ = c.SaveUploadedFile(f, dst)
c.JSON(http.StatusOK, gin.H{
"success": "ok",
})
})
//7.处理404
r.NoRoute(func(c *gin.Context) {
c.JSON(http.StatusNotFound, gin.H{
"code": 404,
"message": "404 Not Found",
"data": nil,
})
})
//8.路由组【支持嵌套】(类似SpringBoot里的@RequestMapping)
userGroup := r.Group("/users")
{
//根据用户id查询用户
userGroup.GET("/:id", func(c *gin.Context) {
id := c.Param("id")
c.JSON(http.StatusOK, gin.H{
"id": id,
"name": "Meta39",
})
})
//创建用户
userGroup.POST("/", func(c *gin.Context) {
var u UserInfo
err := c.ShouldBind(&u)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
}
fmt.Printf("创建用户:%+v\n", u)
c.JSON(http.StatusOK, gin.H{
"success": "ok",
})
})
}
//9.定义中间件,一般放到路由组Group(类似Java中的filter过滤器)
r.GET("/handlerFunc", MyHandlerFunc(true), func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "MyHandlerFunc",
})
})
//10.在控制台输入 go get -u gorm.io/gorm 安装 gorm 软件包
//11.在控制台输入 go get -u gorm.io/driver/mysql 安装 gorm mysql 数据库驱动软件包
dsn := "root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
db, gormErr := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if gormErr != nil {
panic("failed to connect database")
}
// 迁移 schema
_ = db.AutoMigrate(&UserInfo{})
var userInfo UserInfo
// Create
db.Create(&UserInfo{Username: "Meta39", Password: "123456"})
// Read
db.First(&userInfo, 1) // 根据整型主键查找
db.First(&userInfo, "username = ?", "Meta39") // 查找 username 字段值为 Meta39 的记录
// Update - 将 userInfo 的 Password 更新为 654321
db.Model(&userInfo).Update("Password", "654321")
// Update - 更新多个字段
db.Model(&userInfo).Updates(UserInfo{Username: "Meta", Password: "123"}) // 仅更新非零值字段
db.Model(&userInfo).Updates(map[string]interface{}{"Username": "Meta1", "Password": "234"})
// Delete - 删除 userInfo(默认:软删除,即:打上删除标志,如在deleted_at填充时间,表示这条数据已经被删除了)
//db.Delete(&userInfo, 1)
//永久删除
//db.Unscoped().Delete(&userInfo, 1)
//启动服务不指定端口号,则默认:8080
ginErr := r.Run(":8080")
//启动异常处理并输出异常信息
if ginErr != nil {
fmt.Printf("ginErr:%v\n", ginErr)
return
}
}
控制台执行
go run .\main.go
打开浏览器访问localhost:8080/map会输出JSON格式的Hello World
数据库会有一条id = 1的记录