初识gin
Gin 是一个 Go (Golang) 编写的轻量级 http web 框架,运行速度非常快。点击此处访问官方文档。
基本使用
I. 响应
- 入门案例
func main() {
router := gin.Default()
router.GET("/hello", func(c *gin.Context) {
c.String(http.StatusOK, "Hello World!")
})
_ = router.Run(":8080")
// _ = http.ListenAndServe(":8080", router)
}
- 响应json数据
func main() {
router := gin.Default()
router.GET("/hello", func(c *gin.Context) {
type User struct {
UserName string `json:"user_name"` // 自定义json字段名称
Age int `json:"age"`
Password string `json:"-"` // 不去序列化该字段
}
user := User{
UserName: "lc",
Age: 20,
Password: "123456",
}
c.JSON(http.StatusOK, user)
// 直接响应json
//c.JSON(http.StatusOK, gin.H{"user_name": "lc", "age": 23})
})
_ = router.Run(":8080")
}
- 响应xml/yaml数据
func _xml(c *gin.Context) {
c.XML(http.StatusOK, gin.H{"user_name": "lc", "age": 23, "data": gin.H{"name": "test"}})
}
func _yaml(c *gin.Context) {
c.YAML(http.StatusOK, gin.H{"user_name": "lc", "age": 23})
}
func main() {
router := gin.Default()
//router.GET("/json", _json)
router.GET("/xml", _xml)
router.GET("/yaml", _yaml)
_ = router.Run(":8080")
}
- 响应html
// 参考:https://gin-gonic.com/zh-cn/docs/examples/html-rendering/
func _html(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{"username": "lc"})
}
func main() {
router := gin.Default()
// 加载模板目录
router.LoadHTMLGlob("template/*")
router.GET("/html", _html)
_ = router.Run(":8080")
}
- 文件响应
func main() {
router := gin.Default()
// 加载模板目录
router.LoadHTMLGlob("template/*")
// 在go中,可以理解为没有相对文件的路径,只有相对项目的路径
// 访问单个文件
router.StaticFile("/titian", "static/titian.png")
router.StaticFS("/static", http.Dir("static/"))
_ = router.Run(":8080")
}
- 重定向
// 301和302区别: 301重定向通常用于永久性的URL改变,例如网站迁移、改变域名等;302重定向通常用于临时性的内容改变和URL重定向,例如网站维护期间,原URL临时跳转到维护通知页面。
router.GET("/baidu", func(c *gin.Context) {
c.Redirect(301, "https://www.baidu.com")
// 可以跳转自定义路径
// c.Redirect(301, "/html")
})
II. 参数
- 查询参数
func _query(c *gin.Context) {
fmt.Println(c.GetQuery("user"))
fmt.Println(c.Query("user"))
fmt.Println(c.QueryArray("user")) // 拿到多个相同参数
}
// query?id=2&user=lc&user=l3
- 动态参数
func _param(c *gin.Context) {
fmt.Println(c.Param("user_id"))
fmt.Println(c.Param("book_id"))
}
func main() {
router := gin.Default()
router.GET("/param/:user_id", _param)
router.GET("/param/:user_id/:book_id", _param)
_ = router.Run(":8080")
}
// http://localhost:8080/param/12323
- 表单参数
func _form(c *gin.Context) {
fmt.Println(c.PostForm("name"))
fmt.Println(c.PostFormArray("name"))
fmt.Println(c.DefaultPostForm("addr", "四川")) // 如果用户没传,使用默认值
fmt.Println(c.DefaultQuery("age", "27")) // 如果用户没传,使用默认值
forms, err := c.MultipartForm() // 接受所有的form参数
fmt.Println(forms, err)
}
func main() {
router := gin.Default()
router.POST("/form", _form)
_ = router.Run(":8080")
}
- 请求头的各种获取方式
router.GET("/header", func(c *gin.Context) {
fmt.Println(c.GetHeader("User-Agent"))
fmt.Println(c.Request.Header)
// 使用map方式获取需要区分大小写
fmt.Println(c.Request.Header["User-Agent"])
// 设置响应头
// c.Header("token", "112233")
c.JSON(200, gin.H{"msg": "success"})
})
III.绑定参数
gin中的bind可以很方便的将前端传递的数据与结构体进行参数绑定,以及参数校验。在使用这个功能的时候,需要给结构体加上Tag-----json,form,url,xml
- 具体使用
type UserInfo struct {
Name string `json:"name" form:"name" uri:"name"`
Age int `json:"age" form:"age" uri:"age"`
Sex string `json:"sex" form:"sex" uri:"sex"`
}
func main() {
router := gin.Default()
// json
router.POST("/", func(c *gin.Context) {
// 如果校验不通过会返回错误
//c.BindJSON()
var userInfo UserInfo
err := c.ShouldBindJSON(&userInfo)
if err != nil {
c.JSON(200, gin.H{
"msg": "出错了",
})
return
}
c.JSON(200, userInfo)
})
// query
router.POST("/query", func(c *gin.Context) {
var userInfo UserInfo
err := c.ShouldBindQuery(&userInfo)
if err != nil {
c.JSON(200, gin.H{
"msg": "出错了",
})
return
}
c.JSON(200, userInfo)
})
// uri
router.POST("/uri/:name/:age/:sex", func(c *gin.Context) {
var userInfo UserInfo
err := c.ShouldBindUri(&userInfo)
if err != nil {
c.JSON(200, gin.H{
"msg": "出错了",
})
return
}
c.JSON(200, userInfo)
})
// form-data参数
router.POST("/form", func(c *gin.Context) {
var userInfo UserInfo
err := c.ShouldBind(&userInfo)
if err != nil {
c.JSON(200, gin.H{
"msg": "出错了",
})
return
}
c.JSON(200, userInfo)
})
_ = router.Run(":8080")
}
- bind绑定器
常用验证器
required: 必填字段,如: binding:"required"
min 最小长度
max 最大长度
len 长度
eq 等于
ne 不等于
gt 大于
gte 大于等于
lt 小于
lte 小于等于
eqfield 等于其他字符的值
nefield 不等于其他字符的值
- 忽略字段: binding:"-"
gin内置验证器
oneof=man women // 枚举 只能是man women
contains=f // 包含f的字符串
excludes=f // 不包含f的字符串
startswith // 字符串前缀
endswith // 字符串后缀
dive // LikeList []string `json:"like_list" binding:"required,dive,required,startswith=lire"`
ip
uri
url
datetime // 1月2号下午3点4分5秒 在06年
自定义验证的错误信息
// GetValidMsg 获取结构体中的msg参数
func GetValidMsg(err error, obj any) string {
getObj := reflect.TypeOf(obj)
// 将err断言为具体类型
if errs, ok := err.(validator.ValidationErrors); ok {
// 断言成功
for _, e := range errs {
// 循环每一个错误信息
// 根据报错的字段名获取结构体具体字段
if f, exist := getObj.Elem().FieldByName(e.Field()); exist {
msg := f.Tag.Get("msg")
return msg
}
}
}
return err.Error()
}
func main() {
router := gin.Default()
router.POST("/", func(c *gin.Context) {
type User struct {
Name string `json:"name" binding:"required" msg:"用户名校验失败"`
Age int `json:"age" binding:"required" msg:"请输入年龄"`
}
var user User
err := c.ShouldBindJSON(&user)
if err != nil {
c.JSON(200, gin.H{"msg": GetValidMsg(err, &user)})
return
}
c.JSON(200, gin.H{"data": user})
})
_ = router.Run(":8080")
}
自定义验证器
type User struct {
Name string `json:"name" binding:"required,sign" msg:"用户名校验失败"`
Age int `json:"age" binding:"required" msg:"请输入年龄"`
}
func signValid(fl validator.FieldLevel) bool {
var nameList = []string{"ll", "cc", "33"}
for _, nameStr := range nameList {
name := fl.Field().Interface().(string)
if name == nameStr {
return false
}
}
return true
}
func main() {
router := gin.Default()
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
_ = v.RegisterValidation("sign", signValid)
}
router.POST("/", func(c *gin.Context) {
var user User
err := c.ShouldBindJSON(&user)
if err != nil {
c.JSON(200, gin.H{"msg": err.Error()})
return
}
c.JSON(200, gin.H{"data": user})
})
_ = router.Run(":8080")
}
IV. 中间件
- 单个路由中间件
func m1(c *gin.Context) {
fmt.Println("m1...in")
c.Next()
fmt.Println("m1...out")
}
func m2(c *gin.Context) {
fmt.Println("m2...in")
c.Next()
fmt.Println("m2...out")
}
func main() {
router := gin.Default()
router.GET("/", m1, func(c *gin.Context) {
fmt.Println("index...in")
c.JSON(200, gin.H{"msg": "index"})
// 拦截后续响应
//c.Abort()
c.Next() // 类似于栈
fmt.Println("index...out")
}, m2)
_ = router.Run(":8080")
}
// 输出顺序
//m1...in
//index...in
//m2...in
//m2...out
//index...out
//m1...out
// 如果其中一个中间件响应了abort(),后续中间件将不再执行
- 全局中间件和中间件传参
func m10(c *gin.Context) {
fmt.Println("m10...in")
}
func main() {
router := gin.Default()
router.Use(m10)
router.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{"msg": "index"})
})
router.GET("/m10", func(c *gin.Context) {
c.JSON(200, gin.H{"msg": "index"})
})
_ = router.Run(":8080")
}
注:本文档参考枫枫知道大佬的gin教程