22 go语言(golang) - gin框架安装及使用(三)

四、组成

前面的两篇文章中,我们介绍了其中一部分组成,接下来再继续学习:

  1. Router(路由器)

    • Gin 使用基于树结构的路由机制来处理 HTTP 请求。它支持动态路由参数、分组路由以及中间件。
    • 路由器负责将请求路径映射到相应的处理函数。
  2. Context(上下文)

    • gin.Context 是 Gin 中最重要的结构之一,它在请求生命周期内传递信息。
    • Context 提供了对请求和响应对象的访问,以及用于存储数据、设置状态码、返回 JSON 等方法。
  3. Middleware(中间件)

    • 中间件是可以在请求被最终处理之前或之后执行的一段代码,用于实现日志记录、错误恢复、认证等功能。
    • Gin 支持全局中间件和特定路由组或单个路由使用的中间件。
  4. Handlers(处理函数)

    • 处理函数是实际执行业务逻辑的位置,每个路由都会关联一个或多个处理函数。
    • 这些函数接收 gin.Context 参数,通过它们可以获取请求数据并生成响应。
  5. Error Handling(错误处理)

    • Gin 提供了一种机制来捕获和管理应用程序中的错误,可以通过 Context 的方法进行错误报告和恢复操作。
  6. Rendering and Responses(渲染与响应)

    • 支持多种格式的数据输出,包括 JSON、XML 和 HTML 渲染等,方便客户端消费不同类型的数据格式。
  7. Binding and Validation(绑定与验证)

    • 自动将 HTTP 请求中的数据绑定到结构体,并支持对输入数据进行验证,以确保其符合预期格式和规则。
  8. Templates (模板)

    • 虽然不是框架核心,但 Gin 支持集成 HTML 模板引擎,用于生成动态网页内容。

4.6 Rendering and Responses(渲染与响应)

在 Gin 框架中,渲染与响应是处理 HTTP 请求的核心功能之一。Gin 提供了多种方式来生成和发送响应给客户端,包括 JSON、XML、HTML 等格式。

4.6.1 JSON 响应

Gin 提供了简单的方法来返回 JSON 格式的数据,这是 RESTful API 开发中最常用的格式。

go 复制代码
c.JSON(http.StatusOK, gin.H{
    "message": "测试json格式",
    "status":  "success",
})
  • gin.H 是一个快捷方式,用于创建 map[string]interface{} 类型的数据结构。

4.6.2 XML 响应

类似于 JSON,Gin 也支持返回 XML 格式的数据。

go 复制代码
// 使用 c.XML() 方法可以将数据序列化为 XML 格式并发送给客户端。
c.XML(http.StatusOK, gin.H{
    "message": "测试XML格式",
    "status":  "success",
})

4.6.3 HTML 渲染

Gin 支持使用模板引擎渲染 HTML 页面。首先需要加载模板文件,然后在处理函数中进行渲染

  • LoadHTMLGlob() 用于加载模板文件,可以使用通配符指定路径。
  • 在处理函数中,通过调用 c.HTML() 来渲染指定的模板,并传递数据用于填充模板变量。
  • 代码案例在gin的第一篇文章有提到,自行查阅

4.6.4 文件响应

可以直接将文件作为响应发送给客户端,例如下载或显示图片等

go 复制代码
// 使用 c.File() 方法可以直接将服务器上的文件内容发送到客户端。
c.File("./temp_pic.png")

4.6.5 数据流(Streaming)

对于需要逐步生成或传输大块数据的场景,可以使用流模式

go 复制代码
func Test2(t *testing.T) {
	r := gin.Default()

	r.GET("test", func(c *gin.Context) {
		// 模拟数据
		data := []byte("test steaming")
		// 创建一个新的Reader对象,它可以用来从data中读取数据
		reader := bytes.NewReader(data)
		// 计算data的长度,并将其转换为int64类型,这个值将用于HTTP响应的Content-Length头。
		contentLength := int64(len(data))
		// 使用http.DetectContentType函数自动检测data的MIME类型,这个值将用于HTTP响应的Content-Type头。
		contentType := http.DetectContentType(data)

		// 创建一个名为extraHeaders的map,其中 attachment; filename="example.txt" 告诉浏览器应该将响应作为文件下载,文件名为example.txt
		extraHeaders := map[string]string{
			"Content-Disposition": `attachment; filename="example.txt"`,
		}

		c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
	})

	r.Run()
}

使用 DataFromReader() 可以从一个实现了 io.Reader 接口的数据源读取并输出到 HTTP 响应体中。

  • http.StatusOK:HTTP状态码,表示请求成功。
  • contentLength:响应内容的长度。
  • contentType:响应内容的MIME类型。
  • reader:用于读取响应内容的Reader对象。
  • extraHeaders:额外的HTTP头。

4.6.6 重定向(Redirect)

可以通过重定向让客户端访问另一个 URL

go 复制代码
func Test3(t *testing.T) {
	r := gin.Default()
	r.GET("test1", func(c *gin.Context) {
    // 调用 Redirect() 方法设置重定向状态码和目标 URL,让浏览器自动跳转到新的地址。
		c.Redirect(http.StatusMovedPermanently, "test2")
		// 推荐用http自带的code
		// 	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
	})

	r.GET("test2", func(c *gin.Context) {
		fmt.Println("跳转到了test2")
	})

	r.Run()
}

4.7 Binding and Validation(绑定与验证)

在 Gin 框架 中,Binding 和 Validation(绑定与验证)是处理客户端请求数据时的核心功能。Gin 提供了 数据绑定(将请求参数绑定到 Go 结构体)和 数据验证(校验数据是否符合预设规则)机制,简化了开发流程。

  1. 自动数据绑定:支持 JSON、表单、查询参数、URI 参数等多种来源。
  2. 结构化验证 :使用 binding 标签定义字段的验证规则。
  3. 内置规则 :提供丰富的验证规则,如 requiredemailminmax 等。
  4. 自定义验证:支持注册自定义验证器,满足个性化需求。

4.7.1 Binding(数据绑定)

数据绑定的主要作用是将 请求参数(如 JSON、XML、表单数据、查询字符串等)自动绑定到结构体中。

Gin 提供了几种常见的绑定方法

方法 数据来源
c.ShouldBindJSON 请求体中的 JSON 数据
c.ShouldBindQuery URL 查询参数(Query String)
c.ShouldBindForm 表单数据
c.ShouldBindHeader HTTP 请求头
c.ShouldBindUri URL 路径参数(动态路由参数)
c.ShouldBindXML 请求体中的 XML 数据
4.7.1.1 JSON 数据绑定
go 复制代码
// 定义一个结构体,接收 JSON 数据
type LoginInfo struct {
	Username string `json:"username" binding:"required"` // 使用 "binding" 标签进行验证,声明字段为必填项
	Password string `json:"password" binding:"required"`
	Email    string `json:"email"`
}

func Test4(t *testing.T) {
	r := gin.Default()

  // 严格来说应该用r.POST请求,这里只是测试,无所谓了
	r.GET("test", func(c *gin.Context) {
		var loginInfo LoginInfo

		// 绑定 JSON 数据到结构体
		err := c.ShouldBindJSON(&loginInfo)

		fmt.Printf("接受到请求参数:%v", loginInfo)

		if err != nil {
			// 验证失败,返回错误
			c.JSON(http.StatusBadRequest, gin.H{
				"msg":   "请求参数不对",
				"error": err.Error(),
			})
			return
		}
		// 验证通过,正常响应
		c.JSON(200, gin.H{
			"msg":       "登录成功",
			"user_name": loginInfo.Username,
		})

	})

	r.Run()
}

输出

json 复制代码
{
    "msg": "登录成功",
    "user_name": "小明"
}

没有password时

json 复制代码
{
    "error": "Key: 'LoginInfo.Password' Error:Field validation for 'Password' failed on the 'required' tag",
    "msg": "请求参数不对"
}
4.7.1.2 Query 参数绑定

通过 URL 查询参数进行绑定,跟上面的例子差别就只是结构体和bind方法

go 复制代码
type QueryParam struct {
	Page string `form:"page" binding:"required"`
	Size string `form:"size"`
}

func Test5(t *testing.T) {
	r := gin.Default()

	r.GET("test", func(c *gin.Context) {
		var query QueryParam
		err := c.ShouldBindQuery(&query)
		fmt.Printf("接受到请求参数:%v", query)

		if err != nil {
			// 验证失败,返回错误
			c.JSON(http.StatusBadRequest, gin.H{
				"msg":   "请求参数不对",
				"error": err.Error(),
			})
			return
		}
		// 验证通过,正常响应
		c.JSON(200, gin.H{
			"msg": "查询成功",
		})
	})

	r.Run()
}
4.7.1.3 URI 参数绑定

当使用动态路由时,可以绑定路径参数到结构体中

go 复制代码
type UriPrams struct {
	UserName string `uri:"username" binding:"required"`
	UserId   string `uri:"user_id"` // 这种情况下binding:"required"有点多余,因为有些情况下(比如这个参数在最后)如果用户不传这个参数,则可能不能正常访问到这个路由
}

func Test6(t *testing.T) {
	r := gin.Default()

	r.GET("test/:username/:user_id", func(c *gin.Context) {
		var uriPrams UriPrams
		err := c.ShouldBindUri(&uriPrams)
		fmt.Printf("接受到请求参数:%v", uriPrams)

		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"msg":   "请求参数不对",
				"error": err.Error(),
			})
			return
		}
		c.JSON(200, gin.H{
			"msg": "请求成功",
		})
	})

	r.Run()
}

4.7.2 Validation(数据验证)

Gin 使用 go-playground/validator 库作为验证器,通过结构体标签(binding 标签)定义验证规则。

常见验证规则:

验证标签 含义
required 必填字段
min=X 最小值(数字)/ 最小长度(字符串)
max=X 最大值(数字)/ 最大长度(字符串)
email 必须是合法的邮箱格式
len=X 长度必须等于 X
gte=X / lte=X 大于等于 / 小于等于
oneof=X Y Z 值必须是 X、Y、Z 中的一个
4.7.2.1 验证规则
go 复制代码
type Register struct {
	Username string `json:"username" binding:"required,min=1,max=15"`
	Password string `json:"password" binding:"required,min=6"`
	Email    string `json:"email" binding:"required,email"`
	Phone    string `json:"phone" binding:"min=11,max=11"`      // 如果是string类型,有了这个min和max,默认就是必填的了
	Age      int    `json:"age" binding:"min=0,max=90"`         // 但是如果是int类型,就没有这个限制
	Age2     int    `json:"age2" binding:"gte=18"`              // 也是默认必填
	Gender   string `json:"gender" binding:"oneof=male female"` // 也是默认必填
}

func Test7(t *testing.T) {
	r := gin.Default()

	r.POST("register", func(c *gin.Context) {
		var register Register

		err := c.ShouldBindJSON(&register)

		fmt.Println(register)

		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"msg":   "注册失败",
				"error": err.Error(),
			})
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"msg": fmt.Sprintf("恭喜,注册成功:%s", register.Username),
		})

	})

	r.Run()
}
4.7.2.2 自定义验证器

你也可以注册自定义验证器来满足特殊需求,比如验证密码设置的规则:密码长度为8-16位,必须包含大小写英文字母和数字

1、自定义验证规则需要实现gin.Validator接口

2、在Gin的全局配置中注册这个自定义验证器

go 复制代码
type Register2 struct {
	Username string `json:"username" binding:"required,min=1,max=15"`
	Password string `json:"password" binding:"required,password"`
}

// 自定义验证器
var v = func(field validator.FieldLevel) bool {
	// 密码长度为8-16位,必须包含大小写英文字母和数字

	str := field.Field().String()
	if len(str) > 16 || len(str) < 8 {
		return false
	}

	var hasLowerCase bool
	var hasUpperCase bool
	var hasNumber bool

	for _, s := range str {
		if unicode.IsLower(s) {
			hasLowerCase = true
			continue
		}
		if unicode.IsUpper(s) {
			hasUpperCase = true
			continue
		}
		if unicode.IsDigit(s) {
			hasNumber = true
		}
	}

	return hasLowerCase && hasUpperCase && hasNumber
}

func Test8(t *testing.T) {
	r := gin.Default()

	// 注册自定义验证器
	engine := binding.Validator.Engine().(*validator.Validate)
	err := engine.RegisterValidation("password", v)
	if err != nil {
		return
	}

	r.POST("register", func(c *gin.Context) {
		var register Register2

		err := c.ShouldBindJSON(&register)

		fmt.Println(register)

		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"msg":   "注册失败",
				"error": err.Error(),
			})
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"msg": fmt.Sprintf("恭喜,注册成功:%s", register.Username),
		})

	})
	r.Run()
}

4.8 Templates(模板)

Gin 框架提供了基本的模板渲染功能,允许开发者在服务器端生成 HTML 页面。Gin 的模板功能是基于 Go 语言的标准库 html/template 实现的。

  1. 模板文件 :通常是 .tmpl.html 文件,包含 HTML 和嵌入其中的动态内容占位符。

  2. 加载模板:使用 Gin 提供的方法将这些文件加载到应用程序中。

  3. 渲染模板:在处理请求时,将数据传递给已加载的模板进行渲染,并将结果发送给客户端。

4.8.1 创建模板文件

首先,在项目目录下创建一个目录来存放你的 HTML 模板文件,例如 templates/

模板文件通常以 .tmpl.html 为后缀,包含静态 HTML 和动态内容占位符。动态内容通过双大括号 {``{ }} 包裹。在这个示例中,.Title.Name 是数据占位符,它们将在渲染时被实际的数据替换。

html 复制代码
<!-- templates/index.tmpl -->
<!DOCTYPE html>
<html>
<head>
    <title>{{ .Title }}</title>
</head>
<body>
    <h1>Hello, {{ .Name }}!</h1>
</body>
</html>

4.8.2 加载和渲染模板

1、使用 LoadHTMLGlob()LoadHTMLFiles() 方法来加载这些模板。

2、在处理请求时,使用上下文对象中的 c.HTML() 方法来渲染并返回 HTML 响应。

  • HTTP 状态码(如 http.StatusOK)
  • 模板名称(即你要渲染的具体模板)
  • 数据对象(通常为一个 map 或结构体),用于填充模板中的动态内容。
go 复制代码
func Test9(t *testing.T) {
	r := gin.Default()

	// 加载 templates 目录下所有以 .tmpl 为后缀名的文件
	r.LoadHTMLGlob("templates/*")

	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"Title": "Home",
			"Name":  "World",
		})
	})

	r.Run()
}
相关推荐
小黄银技术栈几秒前
校园点餐订餐外卖跑腿Java源码
java·开发语言
向宇it1 分钟前
【从零开始入门unity游戏开发之——C#篇11】一个标准 C# 程序介绍、新的值类型——枚举
开发语言·vscode·unity·c#·游戏引擎
安年CJ7 分钟前
Python 中的指针:深入理解与应用
运维·开发语言·python
我是Superman丶21 分钟前
【自动化】Python SeleniumUtil 油猴 工具 自动安装用户脚本
开发语言·python
抓哇FullStack-Junior28 分钟前
设计模式——原型模式
java·开发语言·设计模式·原型模式
墨绿色的摆渡人1 小时前
用 Python 从零开始创建神经网络(十七):回归(Regression)
开发语言·人工智能·python·深度学习·神经网络·回归
长安游1 小时前
2D gaussian splatting的配置和可视化
开发语言·python
AI大模型训练家2 小时前
JAVA没有搞头了吗?
java·开发语言·python·面试·职场和发展·性能优化
ezreal_pan2 小时前
ShardingSphere-Proxy 连接实战:从 Golang 原生 SQL 到 GORM 的应用
golang·shardingsphere·分库分表
CHHC18802 小时前
golang 使用gzip对json例子
开发语言·golang·json