GO-GIN框架-随笔1
GIN框架是一款流行的web框架,它能提供一个高性能的HTTP路由引擎,GIN是基于HTTP/NET标准库开发的,提供更多功能和更简洁的API
安装:
安装命令:go get -u github.com/gin-gonic/gin可能会出现安装失败:“go: module github.com/gin-gonic/gin: Get "https://proxy.golang.org/github.com/gin-gonic/gin/@v/list": dial tcp xxx:xxx: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.”
代表当前设备无法连接到GO官方的模块代理
proxy.golang.org,原因是 proxy.golang.org的IP地址被干扰或阻断导致连接超时。解决办法:配置国内端口代理:
windows下执行:
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
其中go env -w 代表永久修改环境变量
GO111MODULE=ON代表启用go的module特性
go clean -modcache 清理缓存之后重新执行安装命令
GIN框架特性:
- 快速:小内存占用,可预测的API性能
- 支持中间件
- Crash处理:可以catch一个发生在HTTP请求中的panic并recover它,这样服务端始终可用
- Json验证:可以解析并验证请求的JSON,检查所需值的存在
- 路由组
- 错误管理
- 内置渲染
- 可扩展性
定义路由(代码示例):
1 package main 2 3 import ( 4 "github.com/gin-gonic/gin" 5 ) 6 7 func main() { 8 // 创建一个默认的路由引擎 9 r := gin.Default() 10 // 定义一个 GET 请求的路由 11 // 当访问 /user/getInfo时,会执行这个匿名函数 12 //go run main.go后浏览器访问:localhost:8899/user/getInfo 13 r.GET("/user/getInfo", func(c *gin.Context) { 14 c.JSON(200, gin.H{ 15 "msg": "success", 16 }) 17 }) 18 //定义当前API端口 19 r.Run(":8899") 20 }
Binding和Validate:
Gin框架中的binding验证器为程序提供了简单的数据绑定和校验功能,用于请求参数的数据验证,通过在结构体后面加bind:"规则"来声明该参数的校验要求
validate是通过底层validator包实现的,主要做
通过使用binding和validate标签,我们可以确保API接收到的数据合法性和完整性,并且自定义验证器和错误处理机制可以进一步提高数据校验的灵活度和用户体验,
不同数据源binding绑定方法:
| 绑定方法 | 数据源 | 适用的请求 |
| ShouldBind | 按Content-Type自动选择 | GET/POST都可以 |
| ShouldBindQuery | URL中的参数 | GET请求专用 |
| ShouldBindJson | json body | POST/PUT |
| ShouldBindForm | 表单数据 | POST表单请求 |
| ShouldBindWith | 指定绑定器 | 不限 |
binding基础用法:
type Personstruct { UserName string `binding:"required"` // 必填 Age int `binding:"min=18,max=60"` // 年龄范围 Email string `binding:"required,email"` // 必填且必须是邮箱格式 Password string `binding:"required,min=6,max=20"` // 必填,长度6-20 }
binding的验证规则:
基础校验:
binding:"required" //字段必须存在且不为空
binding:"-"//忽略该字段不进行校验
binding:"omitempty"//若该字段有值则进行校验,无值则跳过校验
字符串校验:
binding:"max=xxx" //最大长度,binding:"max=20"最大长度不可以超过20
binding:"min=xxx"//最小长度,,binding:"min=1"最小长度不可以低于1
binding:"len=xxx"//固定长度,binding:"len=11" 固定长度11,适用于手机号验证
binding:"email"//邮箱格式校验
binding:"url"//url格式校验
binding:"contains=xxx"//包含指定字符串校验,比如binding:"contains=@Amo"校验该字段的内容是否包含‘@Amo’这个关键词
binding:"containsany=xxx"//包含任意字符,比如binding:"containsany=!@#"校验该字段的内容是否包含!@#这些特殊字符
binding:"excludes=xxx"//不包含指定字符串,binding:"excludes=password"
binding:"startswith=xxx"//以指定前缀开始,比如必须以http开头 binding:"startswith=http"
binding:"endswith=xxx"//以指定后缀,比如必须以.com结尾 binding:"endswith=.com"
数值校验:
binding:"eq=xxx"//等于,binding:"eq=5" 当前参数值只能等与5
binding:"ne=xxx"//不等于,binding:"ne=0" 当前参数不可以等于0
binding:"gt=xxx"//大于,binding:"gt=1" 当前参数值要大于1
binding:"gte=xxx"//大于等于
binding:"lt=xxx"//小于
binding:"lte=xxx"//小于等于
binding:"oneof"//枚举值之一,binding:"oneof=1 2 3"
我们通过不使用绑定器的代码和使用绑定器的代码做对比
跨字段校验:
binding:"eqfield=xxx"//当前字段等于另一个字段,binding:"eqfield=Password"
binding:"nefield=xxx"//不等于另一个字段
binding:"ltfield=xxx"//小于另一个字段
binding:"gtfield=xxx"//大于另一个字段
其他格式校验:
binding:"ip"//IP校验 当前字段的值为IP
binding:"ipv4"//IPV4
binding:"ipv6"//IPV6
binding:"mac"//MAC地址校验
binding:"datetime=xxx"//指定格式的日期校验,binding:"datetime=2026-03-11"
binding:"unique"//数组唯一元素
以上校验格式的代码样例:
type RegisterRequest struct { Username string `json:"username" binding:"required,min=3,max=20"` Password string `json:"password" binding:"required,min=6,max=30"` ConfirmPass string `json:"confirm_pass" binding:"required,eqfield=Password"` Email string `json:"email" binding:"required,email"` Age int `json:"age" binding:"gte=0,lte=150"` Gender string `json:"gender" binding:"oneof=male female other"` Phone string `json:"phone" binding:"omitempty,len=11"` Website string `json:"website" binding:"omitempty,url"` Hobbies []string `json:"hobbies" binding:"dive,required,min=2"` }
使用绑定器和不使用绑定器的代码对比来了解:
不使用绑定器:适合多个数据源混合使用
package main
import (
"strconv"
"github.com/gin-gonic/gin"
)
type Person struct {
UserName string `form:"user_name" json:"user_name" binding:"required"`
Avg int `form:"avg" json:"avg" default:"0"` // 注意:default 只在 form 有效
}
func main() {
r := gin.Default()
r.GET("/user/getInfo", func(c *gin.Context) {
var req Person
// 手动处理默认值
req.UserName = c.Query("user_name")
if req.UserName == "" {
c.JSON(400, gin.H{"msg": "user_name is required"})
return
}
// 处理 avg 默认值
avgStr := c.DefaultQuery("avg", "0")
req.Avg, _ = strconv.Atoi(avgStr)
c.JSON(200, gin.H{
"msg": "success",
"user": req.UserName,
"avg": req.Avg,
})
})
r.Run(":8899")
}
绑定器相关代码样例(以ShouldBindQuery为例):
package main
import (
"github.com/gin-gonic/gin"
)
type Person struct {
//获取user_name传参,绑定必填校验
UserName string `form:"user_name" binding:"required"`
//获取avg的传参
Avg int `form:"avg"`
}
func main() {
r := gin.Default()
r.GET("/user/getInfo", func(c *gin.Context) {
var req Person
//使用 ShouldBindQuery 明确从查询参数绑定,其他方法使用参考下方解析
if err := c.ShouldBindQuery(&req); err != nil {
c.JSON(-1, gin.H{
"msg": "校验不通过" + err.Error(),
})
} else {
c.JSON(200, gin.H{
"msg": "success",
})
}
})
r.Run(":8899")
}
场景一:http://localhost:8899/user/getInfo?user_name=&avg=
请求结果:{"msg":"校验不通过Key: 'Person.UserName' Error:Field validation for 'UserName' failed on the 'required' tag"}
场景二:http://localhost:8899/user/getInfo?user_name=阿陌吖&avg=
请求结果:{"msg":"success"}
绑定器相关代码样例(以ShouldBindWith为例):
package main
import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
type Person struct {
UserName string `form:"user_name" binding:"required"`
Avg int `form:"avg"`
}
func main() {
r := gin.Default()
r.GET("/user/getInfo", func(c *gin.Context) {
var req Person
// 指定使用 query 绑定器
if err := c.ShouldBindWith(&req, binding.Query); err != nil {
c.JSON(400, gin.H{"msg": err.Error()})
return
}
c.JSON(200, gin.H{
"msg": "success",
"user": req.UserName,
"avg": req.Avg,
})
})
r.Run(":8899")
}
完整的代码样例
//main.go文件
package main
import (
"gin_pro_test/models/user"
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
// 定义一个 GET 请求的路由
// 当访问 /user/getInfo时,会执行这个匿名函数
r.GET("/user/getInfo", func(c *gin.Context) {
status, msg := user.ValidatPerson(c)
if status && msg == "" {
c.JSON(200, gin.H{"msg": status})
} else {
c.JSON(500, gin.H{"msg": msg})
}
return
})
r.Run(":8899")
}
//UserModel.go
package user
import (
"fmt"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
)
type Person struct {
UserName string `form:"user_name" binding:"required"`
Password string `form:"password" binding:"required"`
Email string `form:"email" binding:"required,email"`
Phone string `form:"phone" binding:"required,len=11"`
}
var PersonMap = map[string]string{
"UserName": "用户名",
"Password": "密码",
"Email": "邮箱",
"Phone": "手机号",
}
func GetPresonName(field string) string {
if name, ok := PersonMap[field]; ok {
return name
}
return field
}
func ValidatPerson(c *gin.Context) (bool, string) {
var param Person
if err := c.ShouldBindQuery(¶m); err != nil {
if es, ok := err.(validator.ValidationErrors); ok {
var errorMessage []string
for _, e := range es {
switch e.Tag() {
case "required":
errorMessage = append(errorMessage, fmt.Sprintf("%s 是必填项", GetPresonName(e.Field())))
case "gte":
errorMessage = append(errorMessage, fmt.Sprintf("%s 必须大于等于 %s", GetPresonName(e.Field()), e.Param()))
case "lte":
errorMessage = append(errorMessage, fmt.Sprintf("%s 必须小于等于 %s", GetPresonName(e.Field()), e.Param()))
case "email":
errorMessage = append(errorMessage, fmt.Sprintf("邮箱格式不正确"))
case "len":
if e.Field() == "Phone" {
errorMessage = append(errorMessage, fmt.Sprintf("手机号为11位字符"))
} else {
errorMessage = append(errorMessage, fmt.Sprintf("%s 必须为 %s 长度", GetPresonName(e.Field()), e.Param()))
}
}
}
return false, strings.Join(errorMessage, ";")
}
return false, fmt.Sprintf("参数绑定错误 %v", err.Error())
}
return true, ""
}
请求参数:http://localhost:8899/user/getInfo?user_name=&avg=&password=11&email=11&phone=111
返回结果:{"msg":"邮箱格式不正确;手机号为11位字符"}
请求参数2:http://localhost:8899/user/getInfo?user_name=%E9%98%BF%E9%99%8C&password=11&[email protected]&phone=11111111111
返回结果2:{"msg":true}
Gin怎么和mysql通信:
引入依赖包:go get -u github.com/go-sql-driver/mysql
数据库配置;
package main import ( "database/sql" "fmt" "log" ) func main() { // 创建一个默认的路由引擎 dsn := "root:password@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local" // 打开数据库连接(注意:只是初始化连接池,不会真正连接) db, err := sql.Open("mysql", dsn) if err != nil { log.Fatal("数据库连接失败:", err) } defer db.Close() // 程序结束时关闭连接 // 配置连接池 db.SetMaxOpenConns(100) // 最大打开连接数 db.SetMaxIdleConns(10) // 最大空闲连接数 // 真正测试连接 if err = db.Ping(); err != nil { log.Fatal("数据库无法访问:", err) } fmt.Println("成功连接到数据库!") }
定义数据模型;
type User struct { ID int `json:"id"` Username string `json:"username"` Email string `json:"email"` Age int `json:"age"` CreatedAt time.Time `json:"created_at"` }
编写增删改查SQL;
//直接写入
func insertUser(db *sql.DB, user User) error { sqlStr := "INSERT INTO users (username, email, age) VALUES (?, ?, ?)" result, err := db.Exec(sqlStr, user.Username, user.Email, user.Age) if err != nil { return fmt.Errorf("插入失败: %v", err) } // 获取影响的行数 rowsAffected, _ := result.RowsAffected() fmt.Printf("插入成功,影响行数: %d\n", rowsAffected) return nil }
//获取写入的自增ID func insertUserGetID(db *sql.DB, user User) (int64, error) { sqlStr := "INSERT INTO users (username, email, age) VALUES (?, ?, ?)" result, err := db.Exec(sqlStr, user.Username, user.Email, user.Age) if err != nil { return 0, fmt.Errorf("插入失败: %v", err) } // 获取自增ID id, err := result.LastInsertId() if err != nil { return 0, fmt.Errorf("获取ID失败: %v", err) } fmt.Printf("插入成功,新用户ID: %d\n", id) return id, nil }
//通过prepare写入 func insertUserPrepared(db *sql.DB, user User) error { // 预编译SQL语句 stmt, err := db.Prepare("INSERT INTO users (username, email, age) VALUES (?, ?, ?)") if err != nil { return err } defer stmt.Close() // 记得关闭 }
注意:在go中func名称开头大写,比如AddUser()表示该方法为公开方法,如果开头小写比如addUser则表示为私有方法,只能在同一个包内调用

浙公网安备 33010602011771号