Gin的笔记

1 创建一个gin项目

配置 GoSDK的 root

1.1 创建项目目录

直接创建一个文件夹使用GoLand打开

1.2 生成mod文件

终端执行

go mod init 项目名称

1.3 安装gin的相关包

  • 配置代理
    setting – go – module
GOPROXY=https://goproxy.cn,direct
  • 安装gin相关包
终端执行命令
go get -u github.com/gin-gonic/gin
// 等待安装完成

2 简单的启动文件

2.1 创建启动文件

在项目目录下面创建启动文件 main.go

package main

// 导入 gin使用的相关包
import (
	"github.com/gin-gonic/gin"
)

func Index(c *gin.Context) {
	c.JSON(200, gin.H{
		"msg":  "this is index of gin !",
		"data": "cmcc",
	})
}

func main() {
	// 1 创建一个gin项目的启动对象
	ginServer := gin.Default()
	// 2 创建一个访问路由
	// 放回一些数据
	ginServer.GET("/hello", func(context *gin.Context) {
		context.JSON(200, gin.H{
			"msg": "this is go for gin !",
		})
	})
	// 路由绑定到一个函数上面
	ginServer.GET("/index", Index)
	// 3 启动gin的服务
	ginServer.Run("127.0.0.1:8080")
}

2.2 创建服务的步骤

  • 1 导入相关使用的包
import(
	"github.com/gin-gonic/gin"
)
  • 2 创建一个gin服务对象
ginServer := gin.Default()
  • 3 绑定一个路由和处理函数

func Index(c *gin.Context) {
	c.JSON(200, gin.H{
		"msg":  "this is index of gin !",
		"data": "cmcc",
	})
}

----------------
ginServer.GET("/hello", func(context *gin.Context) {
		context.JSON(200, gin.H{
			"msg": "this is go for gin !",
		})
	})
	// 路由绑定到一个函数上面
ginServer.GET("/index", Index)
  • 4 配置并启动服务
ginServer.Run("127.0.0.1:8080")

2.3 服务结果

在这里插入图片描述

2.4 添加使用一个icon

2.4.1 安装指定的包

go get github.com/thinkerou/favicon

2.4.2 指定使用的icon

// 添加一个图标  - 本地地址的一个图标
ginServer.Use(favicon.New("./favicon.ico"))

效果:
在这里插入图片描述

2.3 总结 – 服务步骤

  • 1 创建一个路由(可以自定义也可以使用默认的)
  • 2 创建一个匹配的路由并且绑定该路由的处理函数(功能一般在处理函数中来实现)
  • 3 监听地址和端口号(启动这个服务)

3 视图

3.1 状态码

表示服务其状态的一个编码

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func index(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"date": "20230408",
		"msg":  "Hello World!",
	})

	// 状态码 go中自定义的常见的常态码 通过 http.状态名 来导出 PS:http.StatusOK 相当于 200
	/*
		const (
			StatusContinue           = 100 // RFC 9110, 15.2.1
			StatusSwitchingProtocols = 101 // RFC 9110, 15.2.2
			StatusProcessing         = 102 // RFC 2518, 10.1
			StatusEarlyHints         = 103 // RFC 8297

			StatusOK                   = 200 // RFC 9110, 15.3.1
			StatusCreated              = 201 // RFC 9110, 15.3.2
			StatusAccepted             = 202 // RFC 9110, 15.3.3
			StatusNonAuthoritativeInfo = 203 // RFC 9110, 15.3.4
			StatusNoContent            = 204 // RFC 9110, 15.3.5
			StatusResetContent         = 205 // RFC 9110, 15.3.6
			StatusPartialContent       = 206 // RFC 9110, 15.3.7
			StatusMultiStatus          = 207 // RFC 4918, 11.1
			StatusAlreadyReported      = 208 // RFC 5842, 7.1
			StatusIMUsed               = 226 // RFC 3229, 10.4.1

			StatusMultipleChoices   = 300 // RFC 9110, 15.4.1
			StatusMovedPermanently  = 301 // RFC 9110, 15.4.2
			StatusFound             = 302 // RFC 9110, 15.4.3
			StatusSeeOther          = 303 // RFC 9110, 15.4.4
			StatusNotModified       = 304 // RFC 9110, 15.4.5
			StatusUseProxy          = 305 // RFC 9110, 15.4.6
			_                       = 306 // RFC 9110, 15.4.7 (Unused)
			StatusTemporaryRedirect = 307 // RFC 9110, 15.4.8
			StatusPermanentRedirect = 308 // RFC 9110, 15.4.9

			StatusBadRequest                   = 400 // RFC 9110, 15.5.1
			StatusUnauthorized                 = 401 // RFC 9110, 15.5.2
			StatusPaymentRequired              = 402 // RFC 9110, 15.5.3
			StatusForbidden                    = 403 // RFC 9110, 15.5.4
			StatusNotFound                     = 404 // RFC 9110, 15.5.5
			StatusMethodNotAllowed             = 405 // RFC 9110, 15.5.6
			StatusNotAcceptable                = 406 // RFC 9110, 15.5.7
			StatusProxyAuthRequired            = 407 // RFC 9110, 15.5.8
			StatusRequestTimeout               = 408 // RFC 9110, 15.5.9
			StatusConflict                     = 409 // RFC 9110, 15.5.10
			StatusGone                         = 410 // RFC 9110, 15.5.11
			StatusLengthRequired               = 411 // RFC 9110, 15.5.12
			StatusPreconditionFailed           = 412 // RFC 9110, 15.5.13
			StatusRequestEntityTooLarge        = 413 // RFC 9110, 15.5.14
			StatusRequestURITooLong            = 414 // RFC 9110, 15.5.15
			StatusUnsupportedMediaType         = 415 // RFC 9110, 15.5.16
			StatusRequestedRangeNotSatisfiable = 416 // RFC 9110, 15.5.17
			StatusExpectationFailed            = 417 // RFC 9110, 15.5.18
			StatusTeapot                       = 418 // RFC 9110, 15.5.19 (Unused)
			StatusMisdirectedRequest           = 421 // RFC 9110, 15.5.20
			StatusUnprocessableEntity          = 422 // RFC 9110, 15.5.21
			StatusLocked                       = 423 // RFC 4918, 11.3
			StatusFailedDependency             = 424 // RFC 4918, 11.4
			StatusTooEarly                     = 425 // RFC 8470, 5.2.
			StatusUpgradeRequired              = 426 // RFC 9110, 15.5.22
			StatusPreconditionRequired         = 428 // RFC 6585, 3
			StatusTooManyRequests              = 429 // RFC 6585, 4
			StatusRequestHeaderFieldsTooLarge  = 431 // RFC 6585, 5
			StatusUnavailableForLegalReasons   = 451 // RFC 7725, 3

			StatusInternalServerError           = 500 // RFC 9110, 15.6.1
			StatusNotImplemented                = 501 // RFC 9110, 15.6.2
			StatusBadGateway                    = 502 // RFC 9110, 15.6.3
			StatusServiceUnavailable            = 503 // RFC 9110, 15.6.4
			StatusGatewayTimeout                = 504 // RFC 9110, 15.6.5
			StatusHTTPVersionNotSupported       = 505 // RFC 9110, 15.6.6
			StatusVariantAlsoNegotiates         = 506 // RFC 2295, 8.1
			StatusInsufficientStorage           = 507 // RFC 4918, 11.5
			StatusLoopDetected                  = 508 // RFC 5842, 7.2
			StatusNotExtended                   = 510 // RFC 2774, 7
			StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
		)
	*/
}
func main() {
	r := gin.Default()
	r.GET("/index", index)

	http.ListenAndServe("0.0.0.0:8080", r)
}

3.2 响应

3.2.1 String

相应的是直接是谁string类型的数据 – 直接展示在页面上

// 路由
r.GET("/string", _string)

// 处理函数
func _string(c *gin.Context) {
	// 状态码和响应的 字符串数据
	c.String(http.StatusOK, "this is response string data!")
}

在这里插入图片描述

3.2.2 JSON

gin将数据转成JSON类型的数据返回

  • 方法1 字节直接写JSON类型的数据
// 路由
r.GET("/json2", _json2)

// 处理函数
// 不通过结构体构造json数据
func _json2(c *gin.Context) {
	// 通过放在 gin.H 中构造出一个json数据
	c.JSON(200, gin.H{
		"data": "111111",
		"age":  "2222",
	})
}

在这里插入图片描述

  • 方法2 通过构造结构体的形式,返回一个对象型的数据 – json类型数据
// 路由
r.GET("/json", _json)

// 处理函数
func _json(c *gin.Context) {
	// 创建一个相应的结构体
	type User struct {
		Name     string `json:"username"`
		Age      int    `json:"age"`
		Password string `json:"-"`
	}
	// Name     string `json:"username"`  标识转成json类型后字段命名 Name -- > username
	// Password string `json:"-"`		  - 转成json数据后不显示 隐藏掉(保密数据)

	// 实例化结构体
	user := User{"JJJJSON", 20, "123456"}
	c.JSON(http.StatusOK, user)

}

在这里插入图片描述

  • 方法3 直接响应一个map
// 路由
r.GET("/json_map", _jsonMap)
// 处理函数
func _jsonMap(c *gin.Context) {
	// 直接响应一个map
	userMap := make(map[string]interface{})
	userMap["name"] = "AAAAAA"
	userMap["age"] = 30
	userMap["password"] = "123456"

	c.JSON(200, userMap)
}

在这里插入图片描述

3.2.3 HTML

响应一个HTML文件 – 也可以返回数据

// 1 先加载HTML文件
// 响应模板数据
// 先要使用 LoadHTMLGlob()或者LoadHTMLFiles()方法来加载模板文件
r.LoadHTMLGlob("templates/index/*")


// 2 指定路由
r.GET("/html", _html)

// 3 处理函数
// 加载html
func _html(c *gin.Context) {
	// 响应HTML 文件名 携带的数据
	c.HTML(200, "index.html", gin.H{
		"name": "HTML",
		"age":  99,
	})
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>This is Template</title>
</head>
<body>

<h1>姓名 {{ .name }}</h1>
<h1>姓名 {{ .age }}</h1>

</body>
</html>

效果
在这里插入图片描述

3.2.4 文件响应

响应一个或一些静态文件

// 配置固定的文件访问路径
	r.StaticFS("/static", http.Dir("./static/static"))
	// 第一个是浏览器访问路径   第二个是资源路径   -- 没有定义的是无法访问呢的
	r.StaticFile("img.jpg", "./static/img.jpg")

文件目录
在这里插入图片描述
访问 配置的图标片路径
在这里插入图片描述
访问 指定的文件内容
在这里插入图片描述

3.2.5 重定向

// 重定向 -- 路由
r.GET("/redirect", _redirect)


// _redict 重定向处理函数
func _redirect(c *gin.Context) {
	// 第一个从定向的状态码  第二个从定向的地址位置(可以是本地也可以是一个网络资源)
	//c.Redirect(302, "https:www.baidu.com")
	c.Redirect(302, "/html")
}

3.3 请求

3.3.1 查询参数(query)

http://127.0.0.1:8080/query?name=fenghan&id=10&id=100
通过查询将数据传入到后端

// 路由
// Query 的查询参数
r.GET("/query", _query)
===============================
// 查询参数处理函数
func _query(c *gin.Context) {
	// 通过阐述路径传入的查询参数接收
	// c.getQuery("key")   获取指定的key的参数值  返回 value 和ok 标记是否有这个查询参数
	// http://127.0.0.1:8080/query?name=fenghan&id=10&id=100
	value, ok := c.GetQuery("name") // ok 标记的是这个参数是是否存在
	if ok {
		fmt.Println("name`s value is ", value)
	}
	// name`s value is  fenghan

	// 多个名字相同的参数获取所有的值
	// c.getQueryArray("id")  获取到所有查询参数key 位 id的值  返回一个string 切片
	values, ok := c.GetQueryArray("id")
	if ok {
		fmt.Println("id`s all value is ", values)
	}
	// id`s all value is  [10 100]
}

3.3.2 动态参数(param)

存在与查询路径上的参数 – 接受的参数都是 string 类型
http://127.0.0.1:8080/param/zzzJHFMJ<JHFKDS3/125

// 路由和路径参数
// 动态参数
// 路径上接收位 :接收变量名  除路径字符外所有的字符
r.GET("/param/:user_id/:book_id", _param)

// 处理函数

// 动态参数
func _param(c *gin.Context) {
	// 接收动态参数
	//  c.Param("user_id")   接收指定参数名的参数 如果指定参数没有传,则为空
	// 结果是一个字符串
	user_id := c.Param("user_id")
	book_id := c.Param("book_id")
	fmt.Println("user_id: ", user_id)
	fmt.Println("book_id: ", book_id)
	if book_id == "125" {
		fmt.Println("shhhhhhhhhhhhhhhhhhhh")
	}
	fmt.Println()
	//user_id:  zzzJHFMJ<JHFKDS3
	//book_id:  125

3.3.3 表单参数(form)

填写表单传入的表单参数

// 路由
r.POST("/form", _form)
======================================
// 处理函数
func _form(c *gin.Context) {
	// 接收和处理表单参数
	//
	name := c.PostForm("name")                  // 接收以恶个名为 name的表单字段
	names := c.PostFormArray("name")            // 接收多个key位name的表单字段 返回一个字符串切片
	adrr := c.DefaultPostForm("adrr", "什么都没有!") // 接受表单参数中指定key的值 没有 给默认值
	fs, err := c.MultipartForm()                // 接收form 表单中的所有key-value字段

	fmt.Println(name)
	fmt.Println(names)
	fmt.Println(adrr)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(fs)
	//XXXX
	//[XXXX zZZZZ]
	//什么都没有!
	//&{map[accc:[1215313] name:[XXXX zZZZZ]] map[]}
}

传入的表单
在这里插入图片描述

3.3.4 解析原始数据

**
解析原始数据使用 c.GetRawData() 获取到提交到后台的body中的数据 c.GetHeader(“content-type”) 获取到请求头的数据
**

  • 通过解析原始数据将提交的json数据转成结构体类型的数据
// 路由
// JSON 数据的解析
r.POST("/json_l", _json_l)
====================================================
 接收穿过来的json数据
func _json_l(c *gin.Context) {
	// c.GetRawData() 来获传递过来的body的数据  返回的是一个byte 的数组
	body, _ := c.GetRawData()
	// 通过 拿到请求头中的Content-Type 确定是那种数据
	contentType := c.GetHeader("content-type")
	// 判断是不是json数据
	switch {
	case contentType == "application/json":
		//  将数据转成指定的结构体的数据
		type Stu struct {
			Name   string `json:"name"` // 绑定的json字段名要和传入的数据字段一样
			Age    int64  `json:"age"`
			Gender string `json:"gender"`
		}
		// 实例化
		stu := new(Stu)
		// 将json数据解析乘结构体
		err := json.Unmarshal(body, &stu)
		if err != nil {
			fmt.Println(err)
		}
		fmt.Println("This is after struct data ==", stu)
		// This is after json data == &{zZZZZ 30 男}
	}
}
  • 通过封装 来实现数据的简单绑定 – 封装解析函数 只需要传入gin对象和 目标结构体对象
// 路由
r.POST("/json_bind", _json_bind)

===============================================================

// 封装的解析函数
// 接收一个*gin.Context  和一个目标结构体
func json_bind(c *gin.Context, obj any) error {
	body, _ := c.GetRawData()
	// 通过 拿到请求头中的Content-Type 确定是那种数据
	contentType := c.GetHeader("content-type")
	// 判断是不是json数据
	switch {
	case contentType == "application/json":
		err := json.Unmarshal(body, &obj)
		if err != nil {
			return err
		}
	}
	return nil

}


// 处理函数
func _json_bind(c *gin.Context) {
	// 创建一个结构体
	type Stu struct {
		Name   string `json:"name"` // 绑定的json字段名要和传入的数据字段一样
		Age    int64  `json:"age"`
		Gender string `json:"gender"`
	}
	// 实例化
	stu := new(Stu)
	err := json_bind(c, stu)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("this is json_bind res_data = ", stu)
	// this is json_bind res_data =  &{zZZZZ 30 男}
}

3.4 请求方式

gin框架空中推荐restful风格的接口实现 ----- 四大请求方式

  • GET 查询
  • POST 添加
  • PUT 修改
  • DELETE 删除

3.4.1 模拟restful风格的例子

package main

import (
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
)

type ArticleModel struct {
	Title       string  `json:"title"`
	Price       float64 `json:"price"`
	Description string  `json:"description"`
}

// 创建一个保存 很多文章的切片
var articles = []ArticleModel{
	{"《西游记》", 109.99, "取经"},
	{"《三国演义》", 88.56, "中国古代各国割据的故事"},
	{"《水浒传》", 96.64, "古代版的黑帮招安"},
}

// 创建一个响应的对象
type Response struct {
	Code int    `json:"code"`
	Data any    `json:"data"`
	Msg  string `json:"msg"`
}

// 所有文章列表
func _getList(c *gin.Context) {
	c.JSON(http.StatusOK, Response{200, articles, "列表请求成功"})
}

// 查询单个文章的信息
func _getArticle(c *gin.Context) {
	// 获取动态参数的
	id_str := c.Param("id")
	id, _ := strconv.ParseInt(id_str, 10, 64)
	fmt.Println(articles[id])
	c.JSON(http.StatusOK, Response{200, articles[id], "单个文章数据!"})
}

func json_bind_new(c *gin.Context, obj any) error {
	body, _ := c.GetRawData()
	contentType := c.GetHeader("content-type")
	if contentType == "application/json" {
		err := json.Unmarshal(body, &obj)
		if err != nil {
			fmt.Println(err)
			return err
		}
	}
	return nil
}

// 添加一个article
func _addArticle(c *gin.Context) {
	// 接收传过来的数据信息
	art := new(ArticleModel)
	err := json_bind_new(c, &art)
	if err != nil {
		c.JSON(http.StatusBadRequest, Response{http.StatusBadRequest, nil, "添加错误"})
	}
	articles := append(articles, *art)
	c.JSON(http.StatusOK, Response{200, articles, "添加成功!"})
}

// 修改一个文章的信息
func _editArticle(c *gin.Context) {
	id, _ := strconv.ParseInt(c.Param("id"), 10, 64)

	art := new(ArticleModel)
	err := json_bind_new(c, &art)
	if err != nil {
		fmt.Println(err)
	} else {
		articles[id] = *art
	}
	c.JSON(http.StatusOK, Response{200, articles, "修改成功!"})

}
func _deleteArticle(c *gin.Context) {
	id, _ := strconv.ParseInt(c.Param("id"), 10, 64)
	articles = append(articles[:id], articles[id+1:]...)
	c.JSON(http.StatusOK, Response{200, articles, "删除成功!"})

}
func main() {
	// 模拟一个文章的增删查改的Restful请求
	r := gin.Default()

	// 获取文章列表
	r.GET("/article_list", _getList)

	// 获取 查询单个文章的信息
	r.GET("/article/:id", _getArticle)

	// 添加一个书的信息
	r.POST("/article", _addArticle)

	// 修改一个书的信息
	r.PUT("/article/:id", _editArticle)

	// 删除一个书的信息
	r.DELETE("/delete_article/:id", _deleteArticle)

	r.Run(":8080")
}

3.4.2 查询书籍列表 – 结果

在这里插入图片描述

3.4.3 查询单个书籍的信息 – 结果

在这里插入图片描述

3.4.4 添加一个书籍信息 – 结果

在这里插入图片描述

3.4.5 修改一个书籍的信息 – 结果

在这里插入图片描述

3.4.6 删除一个书籍 – 结果

在这里插入图片描述

3.5 请求头

发送一个网络请求的时候会产生一个携带请求头数据,一起传入后端

3.5.1 获取请求头数据

// 路由
r.GET("/header", _header)
=====================================================

func _header(c *gin.Context) {
	// 通过 getHeader(Key)  获取到指定请求头的数据  --- 这个里面的key不区分大小写,但是单词之前要用 - 链接
	user_agent := c.GetHeader("user-agent") // 获取请求头中user-agent的数据
	fmt.Println("请求头中的user-agent数据:", user_agent)

	// 获取到 请求头中所有的数据
	// c.Request.Header   -- 返回:map[string][]string  key = 所有值的切片
	headers := c.Request.Header
	fmt.Println("这是所有的请求头信息:", headers)

	// 获取到的所有信息也可以通过通过Get方法拿到指定的值 --  []string一个字符串切片
	get_user_agent := headers.Get("user-agent") // key不区分大小写
	fmt.Println("通过Get方式获取到user-agent:", get_user_agent)

	// 通过map形式取值
	map_user_agent := headers["User-Agent"] //使用map方式取值 key 区分大小写
	fmt.Println("通过map的形式取值:", map_user_agent)

	//请求头中的user-agent数据: zzzzzzzzz
	//这是所有的请求头信息: map[Accept:[*/*] Accept-Encoding:[gzip, deflate, br] Connection:[255313] Content-Length:[40] Content-Type:[ajja] User-Agent:[zzzzzzzzz]]
	//通过Get方式获取到user-agent: zzzzzzzzz
	//通过map的形式取值: [zzzzzzzzz]

}

3.5.2 使用header模拟拦截爬虫

自定义一个header的user-agen 假设含哟python的是爬虫程序

// 路由
// 模拟区分用户和爬虫程序 按照 python 位例子
r.GET("/intercept_header", _interceptHeader)
================================

// 拦截非法的请求头信息  -- 模拟爬虫和用户区别对待
func _interceptHeader(c *gin.Context) {

	// 1 获取到 请求头的User-Agent
	user_agent := c.GetHeader("User-Agent")

	// 2 判断User-Agent 中是否包含python字段类判断是否是 爬虫程序
	if strings.Contains(user_agent, "python") {
		// 返回一个针对爬虫的处理数据
		c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": "这是针对爬虫程序返回的数据!"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": "这是针对正常登录用户返回的数据!"})

}

  • 爬虫程序
    在这里插入图片描述
  • 正常用户
    在这里插入图片描述

3.6 响应头

设置响应的响应头

// 路由
// 响应头
	r.GET("/response_header", _responseHeader)

// _responseHeader 处理响应信息的
func _responseHeader(c *gin.Context) {
	//这设立设置一些响应的信息
	c.Header("Token", "akjsgh&slfjdao789ab") // 设置响应头的数据

	// 也可以通过内置的SetXXX来设置一些信息 ps
	c.JSON(200, gin.H{"msg": "关于响应头!"})
}

==================================
  • 设置响应头的SetXXX方法

  • 结果
    在这里插入图片描述

4 参数绑定

前端以各种形式传来的参数,绑定到结构体中,
gin中的bind可以很方便的将 前端传递 来的数据与 结构体 进行 参数绑定 ,以及参数校验

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

type Student struct {
	Name   string `json:"name" form:"name" uri:"name"`
	Age    int    `json:"age" form:"age" uri:"age"`
	Gender string `json:"gender" form:"gender" uri:"gender"`
}

func main() {
	// 这里参数绑定 和结构体数据的绑定
	// -- 参数绑定的时候需要将 结构体数据加上响应的标签,给不同不请求使用的把标签也不同
	// -- 标签的名字要和传入数据字段名相同

	// 推荐使用Should Bind  可以绑定 json,form,xml,yaml,uri等类型数据
	// 绑定函数会自动校验参数,如果不通过会报错

	r := gin.Default()

	// ShouldBindJSON  绑定传入的json数据
	// 使用的tag 是 `json`
	r.POST("/shouldBindJSON", _shouldBindJSON)

	// ShouldBindQuery 绑定传入的查询参数
	// 使用的参数tag 是 `form`
	r.GET("/shouldBindQuery", _shouldBindQuery)

	// 便规定路径阐述 参数卸载路径上的  使用 tag是 uri
	r.GET("/shouldBindUri/:name/:age/:gender", _shouldBindUri)

	// 绑定 data_form x-www-form-urlencode 类型的数据  -- 使用的tag --  form
	r.POST("/shouldBind", _shouldBind)

	r.Run(":8080")
}

// 绑定json数据
func _shouldBindJSON(c *gin.Context) {
	// 创建按一个结构体实例
	var stu Student
	// 绑定数据
	err := c.ShouldBindJSON(&stu) // 将传入的json数据绑定到 stu这个结构体中 -- 相当于之前自己封装的函数
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"msg": "传入的阐述有问题!"})
	}
	c.JSON(http.StatusOK, stu)
}

// 绑定的是查询阐述
func _shouldBindQuery(c *gin.Context) {
	// 创建一个结构体的实例
	var stu Student
	// 绑定参数
	err := c.ShouldBindQuery(&stu) // 使用tag是 form
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"msg": "传入的阐述有问题!"})
	}
	c.JSON(http.StatusOK, stu)
}

// 绑定路径参数  param/ZAAA/20/男
func _shouldBindUri(c *gin.Context) {
	// 创建结构体的实例
	var stu Student
	// 绑定数据  -- 使用的tag是uri
	err := c.ShouldBindUri(&stu)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"msg": "传入的阐述有问题!"})
	}
	c.JSON(http.StatusOK, stu)
}

// 绑定data-form类型的数据
func _shouldBind(c *gin.Context) {
	var stu Student
	err := c.ShouldBind(&stu) // 使用的tag 也是 form字段
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"msg": "传入的阐述有问题!"})
	}
	c.JSON(http.StatusOK, stu)

}

4.1 ShouldBindJSON

  • 绑定的是自定义的json数据
  • 使用的tag是json
// 路由
// ShouldBindJSON  绑定传入的json数据
// 使用的tag 是 `json`
r.POST("/shouldBindJSON", _shouldBindJSON)
=============================================================
// 处理函数

// 绑定json数据
func _shouldBindJSON(c *gin.Context) {
	// 创建按一个结构体实例
	var stu Student
	// 绑定数据
	err := c.ShouldBindJSON(&stu) // 将传入的json数据绑定到 stu这个结构体中 -- 相当于之前自己封装的函数
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"msg": "传入的阐述有问题!"})
	}
	c.JSON(http.StatusOK, stu)
}
  • 结果
    在这里插入图片描述

4.2 ShouldBindQuery

  • 绑定的是查询参数
  • 使用tag是form
// 路由
// ShouldBindQuery 绑定传入的查询参数
// 使用的参数tag 是 `form`
r.GET("/shouldBindQuery", _shouldBindQuery)

===================================
// 绑定的是查询阐述
func _shouldBindQuery(c *gin.Context) {
	// 创建一个结构体的实例
	var stu Student
	// 绑定参数
	err := c.ShouldBindQuery(&stu) // 使用tag是 form
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"msg": "传入的阐述有问题!"})
	}
	c.JSON(http.StatusOK, stu)
}
  • 结果
    在这里插入图片描述

4.3 ShouldBindUri

  • 绑定路径参数
  • 使用tag是uri
// 路由
// 便规定路径阐述 参数卸载路径上的  使用 tag是 uri
r.GET("/shouldBindUri/:name/:age/:gender", _shouldBindUri)
==================
// 处理函数
// 绑定路径参数  param/ZAAA/20/男
func _shouldBindUri(c *gin.Context) {
	// 创建结构体的实例
	var stu Student
	// 绑定数据  -- 使用的tag是uri
	err := c.ShouldBindUri(&stu)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"msg": "传入的阐述有问题!"})
	}
	c.JSON(http.StatusOK, stu)
}

  • 结果
    在这里插入图片描述

4.4 ShouldBind

  • 可以绑定 data_form,x-www-form-urlencode 类型的数据
  • 使用tag是 form
// 路由
// 绑定 data_form x-www-form-urlencode 类型的数据  -- 使用的tag --  form
r.POST("/shouldBind", _shouldBind)
===================================
// 处理函数
// 绑定data-form类型的数据
func _shouldBind(c *gin.Context) {
	var stu Student
	err := c.ShouldBind(&stu) // 使用的tag 也是 form字段
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"msg": "传入的阐述有问题!"})
	}
	c.JSON(http.StatusOK, stu)

}

  • 结果
    在这里插入图片描述

5 验证器 - 校验参数

校验参数是否符合规则

5.1 常用的验证器

作用与结构体使用binding 的tag进行绑定验证

		常用的数据校验绑定
		// 不能为空,并且不能没有这个字段
		required: 必填字段,如:binding:"required"

		// 针对字符串的长度
		min 最小长度,如:binding:"min=5"
		max 最大长度,如:binding:"max=10"
		len 长度,如:binding:"len=6"

		// 针对数字的大小
		eq 等于,如:binding:"eq=3"
		ne 不等于,如:binding:"ne=12"
		gt 大于,如:binding:"gt=10"
		gte 大于等于,如:binding:"gte=10"
		lt 小于,如:binding:"lt=10"
		lte 小于等于,如:binding:"lte=10"

		// 针对同级字段的
		eqfield 等于其他字段的值,如:PassWord string `binding:"eqfield=Password"`
		nefield 不等于其他字段的值

		- 忽略字段,如:binding:"-"   该字段不进行匹配


		如果不符合以上的规则将会报错
package main

import "github.com/gin-gonic/gin"

func main() {
	// 验证器 -- 校验传入的阐述是否符合规范,作用与 绑定结结构体上
	// 使用的是某个tag 并且指定这个tag的值

	r := gin.Default()

	// 常用的验证器
	r.POST("/usually_check", _uauallyCheck)

	r.Run(":8080")
}
func _uauallyCheck(c *gin.Context) {
	type Users struct {
		Name string `json:"name" binding:"required,min=3,max=12,len=8"`
		// binding:"required"   -> 传入的name字段不能是空,也不能不传
		// binding:"min=3,max=12,len=8"  ->  限制 最短:3 最长:12  长度:8
		Age int `json:"age"`
		// 针对与 数字
		// eq 等于,如:binding:"eq=3"
		// ne 不等于,如:binding:"ne=12"
		// gt 大于,如:binding:"gt=10"
		// gte 大于等于,如:binding:"gte=10"
		// lt 小于,如:binding:"lt=10"
		// lte 小于等于,如:binding:"lte=10"
		Password   string `json:"password"`
		RePassword string `json:"re_password" binding:"eqfield=password"` // re_password要等与password(但是可以为空)
		// 针对同级字段的
		// eqfield 等于其他字段的值,如:PassWord string `binding:"eqfield=Password"`
		// nefield 不等于其他字段的值

	}
	
	var user Users
	err := c.ShouldBindJSON(&user) // 绑定数据
	if err != nil {
		c.JSON(200, gin.H{"msg": err.Error})
		return
	}
	c.JSON(200, user)
}

5.2 gin 内置验证器

// 枚举  只能是red 或green
oneof=red green 

// 字符串  
contains=fengfeng  // 包含fengfeng的字符串
excludes // 不包含
startswith  // 字符串前缀
endswith  // 字符串后缀

// 数组
dive  // dive后面的验证就是针对数组中的每一个元素

// 网络验证
ip
ipv4
ipv6
uri
url
// uri 在于I(Identifier)是统一资源标示符,可以唯一标识一个资源。
// url 在于Locater,是统一资源定位符,提供找到该资源的确切路径

// 日期验证  1月2号下午3点4分5秒在2006年
datetime=2006-01-02

5.3 自定义错误验证

//路由
// 自定义响应错误信息
r.POST("/self_errors", _selfErrors)
=====================================

// 验证器错误信息处理器

// 接受一个 err和一个结构体的地址 返回一个错误信息
func selfCheckMsg(err error, obj any) string {
	// 通过获取错误信息的类型
	errType := reflect.TypeOf(obj)
	// 通过断言来判定errType的内容
	if errs, ok := err.(validator.ValidationErrors); ok {
		// 如果断言成功就进行解析数据的错误 -- 循环遍历错误列表
		for _, e := range errs {
			// 通过的个错误的实例 解析错误信息
			// 通过错误信息找到抛出错误的结构体字段  e.Field() --> 错误的名字
			// errType.Elem().FieldByName() 拿到整个结构团体类型的数据 传入一个字段,找到相对相应字段的 一行信息
			if f, is := errType.Elem().FieldByName(e.Field()); is {

				// 通过结构体数据的一行数据信息 f.Tag.Get("msg") 获取具体Tag的的信息内容  --> msg的内容
				errMsg := f.Tag.Get("msg")
				return errMsg
			}
		}
	}
	return err.Error()

}
func _selfErrors(c *gin.Context) {
	// 自定义错误的的验证
	type UserInfo struct {
		// 后面的msg -> 是自定义的tag 之后出错想要提示的信息内容
		Name string `json:"name" binding:"min=3,max=12" msg:"姓名验证错误!"`
		Age  int    `json:"age" binding:"gt=17" msg:"年龄信息验证错误!"`
	}
	var user UserInfo
	// 绑定JSON数据
	err := c.ShouldBindJSON(&user)
	if err != nil {
		// 如果存在错误信息,说明绑定出现了问题  - 系一个函数处理这个错误信息
		c.JSON(http.StatusOK, gin.H{
			"msg": selfCheckMsg(err, &user),
		})
		return
	}
	c.JSON(http.StatusOK, user)
}
  • 结果
    在这里插入图片描述

5.4 自定义验证器

// 注册验证器
// binding.Validator.Engine().(*validator.Validate)  断言这个验证器
if engine, ok := binding.Validator.Engine().(*validator.Validate); ok {
	// 主车一个名为 sing的验证其 验证函数是 checkValidate
	engine.RegisterValidation("sign", checkValidate)
	... 可以注册多个
}

// 路由
// 自定义验证器
r.POST("/self_check", _selfCheck)

=====================================
// 处理函数
func _selfCheck(c *gin.Context) {
	// 自定义一个验证器
	type SelfUser struct {
		Name string `json:"name" binding:"sign" msg:"姓名已经存在!"`
	}
	var selfuser SelfUser
	err := c.ShouldBindJSON(&selfuser)
	if err != nil {
		c.JSON(200, gin.H{"msg": selfCheckMsg(err, &selfuser)})
		return
	}
	c.JSON(200, selfuser)
}


// 自定义数据验证函数
// 接收一个 f validator.FieldLevel
// 返回一个bool值
func checkValidate(f validator.FieldLevel) bool {
	// 如果名字在这个里面就已经注册过,就报错
	var nameList = []string{"aaa", "bbb", "ccc", "ddd"}
	for _, name := range nameList {
		// 断言传入的数据是string类型
		nameStr := f.Field().Interface().(string)
		if nameStr == name {
			return false
		}
	}
	return true
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值