Golang中如何在自定义验证器内从gin.Context获取URL

Golang中如何在自定义验证器内从gin.Context获取URL 大家好,我正在尝试在自定义验证器中使用 gin.Context 引用来获取 Gin 请求的 URL 路径,但是当我调用 /test2 这个 URL 时,我无法在验证器中获取到 /test1 这个 URL,上下文始终给我的是 /test2 这个 URL,如下图所示;

package main

import (
	"context"
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/validator/v10"
)

// Macro ...
type Macro struct {
	IDMacro   int `db:"idMacro" form:"idMacro" binding:"required_route_oneof=GET;/test1"`
	IDCentral int `db:"idCentral" form:"idCentral" binding:"required_route_oneof=GET;/test1 GET;/test2"`
}

type Validator struct {
	C *gin.Context
}

func (cv *Validator) RequiredRouteOneOf(ctx context.Context, fl validator.FieldLevel) bool {
	fmt.Printf("%s %s\r\n", cv.C.Request.Method, cv.C.Request.URL.Path)
	return true
}

func test1(c *gin.Context) {
	inputData := Macro{}

	if err := c.ShouldBindQuery(&inputData); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
}

func test2(c *gin.Context) {
	inputData := Macro{}

	if err := c.ShouldBindQuery(&inputData); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
}

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

	// Use the custom validator middleware
	r.Use(func(c *gin.Context) {
		context := &Validator{c}
		_ = binding.Validator.Engine().(*validator.Validate).RegisterValidationCtx("required_route_oneof", context.RequiredRouteOneOf)
		c.Next()
	})

	r.GET("/test1", test1)
	r.GET("/test2", test2)

	r.Run(":3752")
}

Captura de tela de 2023-08-05 20-31-32

有人知道为什么会发生这种情况吗?


更多关于Golang中如何在自定义验证器内从gin.Context获取URL的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中如何在自定义验证器内从gin.Context获取URL的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在自定义验证器中获取正确的URL路径,需要将验证器注册移到中间件外部,并使用gin.Context的副本。问题在于每次请求都会重新注册验证器,导致上下文被覆盖。以下是修正后的代码:

package main

import (
	"context"
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/validator/v10"
)

// Macro ...
type Macro struct {
	IDMacro   int `db:"idMacro" form:"idMacro" binding:"required_route_oneof=GET;/test1"`
	IDCentral int `db:"idCentral" form:"idCentral" binding:"required_route_oneof=GET;/test1 GET;/test2"`
}

type contextGetter func() *gin.Context

var getContext contextGetter

func RequiredRouteOneOf(ctx context.Context, fl validator.FieldLevel) bool {
	if getContext != nil {
		c := getContext()
		fmt.Printf("%s %s\r\n", c.Request.Method, c.Request.URL.Path)
	}
	return true
}

func test1(c *gin.Context) {
	inputData := Macro{}

	if err := c.ShouldBindQuery(&inputData); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
}

func test2(c *gin.Context) {
	inputData := Macro{}

	if err := c.ShouldBindQuery(&inputData); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
}

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

	// 注册验证器(只注册一次)
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		_ = v.RegisterValidationCtx("required_route_oneof", RequiredRouteOneOf)
	}

	// 中间件设置当前上下文
	r.Use(func(c *gin.Context) {
		getContext = func() *gin.Context {
			return c
		}
		c.Next()
		// 清理上下文引用
		getContext = nil
	})

	r.GET("/test1", test1)
	r.GET("/test2", test2)

	r.Run(":3752")
}

关键修改:

  1. 将验证器注册移到中间件外部,确保只注册一次
  2. 使用闭包函数getContext动态获取当前请求的上下文
  3. 在中间件中设置和清理上下文引用

这样当访问/test1时,验证器会正确输出GET /test1;访问/test2时输出GET /test2

回到顶部