【Gin实战二】Gin中间件和Go Validator:Web开发中的秘密武器

1,334

1.middleware

在 Web 开发中,Gin 是一个流行的 Go 语言框架,它提供了一种简洁且高效的方式来构建 Web 应用程序。Gin 中的中间件是一种用于处理 HTTP 请求和响应的机制。中间件允许开发者在请求到达处理程序之前或响应发送到客户端之前,对请求和响应进行一些预处理或后处理。

在 Gin 中,中间件是通过使用 Use 方法来注册的。这个方法接受一个或多个中间件函数作为参数。中间件函数在请求被处理之前被调用,并且可以执行一些操作,例如记录请求日志、鉴权、请求参数验证等。中间件函数还可以修改请求或响应,或者在请求处理之后执行一些清理操作。

1.1使用方法

在 Gin 框架中,中间件是用于在请求到达路由处理程序之前或之后执行一些操作的函数。它们可以用于添加日志记录、身份验证、错误处理等功能。下面是 Gin 中间件的使用方法介绍:

  1. 全局中间件:使用Use方法将中间件添加到全局中,对所有的路由请求都起作用。
r := gin.Default()
r.Use(loggerMiddleware)
  1. 路由中间件:使用Group方法创建具有相同中间件的路由组。
authGroup := r.Group("/api", authMiddleware)
authGroup.GET("/users", getUsers)
authGroup.POST("/users", createUser)
  1. 单个路由中间件:在路由处理程序上使用Use方法添加中间件。
r.GET("/hello", authMiddleware, helloHandler)
  1. 中间件函数签名:中间件函数必须具有func(c *gin.Context)的签名,其中c *gin.Context是请求的上下文对象,它包含了请求和响应的相关信息。
func loggerMiddleware(c *gin.Context) {
    // 在处理请求之前执行的操作
    log.Printf("Started %s %s", c.Request.Method, c.Request.URL.Path)

    // 将请求传递给下一个处理程序
    c.Next()

    // 在处理完请求之后执行的操作
    log.Printf("Completed %s in %v", c.Request.URL.Path, time.Since(start))
}
  1. 中间件的执行顺序:中间件按照它们添加的顺序执行,因此顺序很重要。可以使用Use方法多次添加不同的中间件,它们将按照添加的顺序依次执行。
r := gin.Default()
r.Use(loggerMiddleware)
r.Use(authMiddleware)
  1. 中间件中的操作:在中间件函数中,你可以执行各种操作,例如记录日志、验证身份、修改请求或响应等。如果需要停止处理链并返回响应,可以使用c.Abort()方法。
func authMiddleware(c *gin.Context) {
    token := c.GetHeader("Authorization")
    if token != "valid_token" {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
        c.Abort()
        return
    }
    c.Next()
}

通过使用这些方法,你可以使用中间件增加额外的功能、处理请求和响应,并根据需要在不同的路由上应用不同的中间件。这使得 Gin 框架非常灵活和强大。

1.2快速开始

以下是一个使用了Gin中间件的完整代码示例:

package main

import (
	"log"
	"net/http"
	"time"

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

func main() {
	r := gin.Default()

	// 全局中间件
	r.Use(loggerMiddleware)

	// 路由组中间件
	authGroup := r.Group("/api")
	authGroup.Use(authMiddleware)
	{
		authGroup.GET("/users", getUsers)
		authGroup.POST("/users", createUser)
	}

	// 单个路由中间件
	r.GET("/hello", authMiddleware, helloHandler)

	r.Run(":8080")
}

func loggerMiddleware(c *gin.Context) {
	start := time.Now()

	// 在处理请求之前执行的操作
	log.Printf("Started %s %s", c.Request.Method, c.Request.URL.Path)

	// 将请求传递给下一个处理程序
	c.Next()

	// 在处理完请求之后执行的操作
	log.Printf("Completed %s in %v", c.Request.URL.Path, time.Since(start))
}

func authMiddleware(c *gin.Context) {
	token := c.GetHeader("Authorization")
	if token != "valid_token" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
		c.Abort()
		return
	}
	c.Next()
}

func getUsers(c *gin.Context) {
	// 处理获取用户列表的逻辑
	c.JSON(http.StatusOK, gin.H{
		"message": "Get users",
	})
}

func createUser(c *gin.Context) {
	// 处理创建用户的逻辑
	c.JSON(http.StatusOK, gin.H{
		"message": "Create user",
	})
}

func helloHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"message": "Hello, World!",
	})
}

在这个示例中:

  1. loggerMiddleware是一个全局中间件,它记录请求的开始和结束时间。
  2. authMiddleware是一个路由组中间件,它用于验证请求的token。
  3. /api/users/api/users是属于authGroup路由组的路由,它们都将应用authMiddleware中间件。
  4. /hello是一个单独的路由,它应用了authMiddleware中间件。

当你启动应用程序后,可以使用不同的请求来测试中间件的工作原理。例如:

  • 对于没有提供有效token的请求,将返回401 Unauthorized:
GET /api/users
Authorization: invalid_token
  • 对于提供了有效token的请求,将返回相应的处理结果:
GET /api/users
Authorization: valid_token
POST /api/users
Authorization: valid_token
GET /hello
Authorization: valid_token

通过使用这些不同的中间件方法,可以在 Gin 应用程序中添加各种功能。

2.Gin验证器

Go的Validator是一种用于验证数据有效性的库或软件包。它提供了一组验证规则和函数,用于确保输入数据符合特定的要求和约束。通过使用验证器,您可以轻松地验证表单输入、API请求、配置参数等。

在Go语言中,有很多可用的验证库,其中一些受欢迎的包括:

  1. govalidatorgithub.com/asaskevich/…
  2. validatorgithub.com/go-playgrou…
  3. ozzo-validationgithub.com/go-ozzo/ozz…

本文介绍第二种validator,详细的使用指南可以参考官网文档pkg.go.dev/github.com/…

2.1快速开始

这段代码是一个使用 Gin 框架和 go-playground/validator 进行参数验证的示例。

在这段代码中,我们定义了一个 User 结构体,它包含了 NameEmailAge 字段,每个字段都使用了 jsonvalidate 标签来指定 JSON 序列化的字段名和验证规则。

然后,我们创建了一个 Gin 路由器,并注册了一个名为 "/user" 的 POST 路由。

在中间件函数 ValidatorMiddleware 中,我们创建了一个 go-playground/validator 的实例,并将其存储到 Gin 上下文中,以便在后续的处理函数中使用。

在处理函数 handleUser 中,我们首先从 Gin 上下文中获取验证器实例,并使用 c.ShouldBindJSON 方法将请求的 JSON 数据绑定到 user 结构体中。

然后,我们使用验证器对 user 结构体进行验证,如果验证失败,我们返回相应的验证错误响应。

如果验证通过,我们可以进一步处理请求,并返回一个包含成功信息和用户数据的 JSON 响应。

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/go-playground/validator/v10"
    "net/http"
)

type User struct {
    Name  string `json:"name" validate:"required"`
    Email string `json:"email" validate:"email" `
    Age   int    `json:"age" validate:"gte=0,lte=150"`
}

func main() {
    r := gin.Default()

    // 注册自定义验证器中间件
    r.Use(ValidatorMiddleware())

    r.POST("/user", handleUser)

    r.Run(":8090")
}

func ValidatorMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        validate := validator.New()

        // 将验证器存储到 Gin 上下文中
        c.Set("vd", validate)

        c.Next()
    }
}

func handleUser(c *gin.Context) {
    // 从 Gin 上下文中获取验证器
    vd, exists := c.Get("vd")
    if !exists {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Validator not found"})
        return
    }

    // 使用验证器进行参数验证
    var user User
    if err := c.ShouldBindJSON(&user); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    err := vd.(*validator.Validate).Struct(user)
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    // 处理验证通过的逻辑
    c.JSON(http.StatusOK, gin.H{"message": "User registered successfully", "user": user})
}

当我们输入一个错误的邮箱,请求会被拦截,并返回下图信息: