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

发布于:2024-12-19 ⋅ 阅读:(14) ⋅ 点赞:(0)

四、组成

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

  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 开发中最常用的格式。

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

4.6.2 XML 响应

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

// 使用 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 文件响应

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

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

4.6.5 数据流(Streaming)

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

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

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 数据绑定
// 定义一个结构体,接收 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()
}

输出

{
    "msg": "登录成功",
    "user_name": "小明"
}

没有password时

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

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

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 参数绑定

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

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 验证规则
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的全局配置中注册这个自定义验证器

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 是数据占位符,它们将在渲染时被实际的数据替换。

<!-- 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 或结构体),用于填充模板中的动态内容。
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()
}

网站公告

今日签到

点亮在社区的每一天
去签到