使用Gin框架在Golang中验证枚举类型

huangapple go评论72阅读模式
英文:

Validate enum in Golang using Gin framework

问题

我尝试使用Gin框架在Golang中验证枚举是否有效。

我找到了这个解决方案:

但这种方法的缺点是硬编码的值,每次枚举值更改时都需要手动更改。

有没有一种方法可以通过枚举的名称作为字符串来获取枚举,而无需创建和注册类型的映射?

期望的结果:

package main

import "github.com/go-playground/validator"

type Status int

const (
	Single Status = iota
	Married
	Other
)

type User struct {
	Status Status `json:"status" binding:"Enum=Status"`
}

func Enum(fl validator.FieldLevel) bool {
	enumType := fl.Param() // Status
	// 通过 `enumType` 获取 `Status` 并进行验证...
	return true
}

func main() {}
英文:

I try to validate if enum is valid in Golang using Gin framework.

I came across this solution:

But disadvantage of this approach is hardcoded value which we have to change manually every time enum value has changed.

Is there any way to get enum by its name as string without creating and register the map with types?

Desired result:

package main

import "github.com/go-playground/validator"

type Status int

const (
	Single Status = iota
	Married
	Other
)

type User struct {
	Status Status `json:"status" binding:"Enum=Status"`
}

func Enum(fl validator.FieldLevel) bool {
	enumType := fl.Param() // Status
	// get `Status` by `enumType` and validate it...
	return true
}

func main() {}

答案1

得分: 1

以下是翻译好的内容:

解决方案之一可能是:

package main

import (
	"net/http"

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

type Enum interface {
	IsValid() bool
}

type Status int

const (
	Single Status = iota + 1 // 添加 +1,否则验证对于0将不起作用
	Married
	Other
)

func (s Status) IsValid() bool {
	switch s {
	case Single, Married, Other:
		return true
	}

	return false
}

type Input struct {
	RelationshipStatus Status `json:"relationship_status" binding:"required,enum"`
}

func UpdateRelationshipStatus(context *gin.Context) {
	input := Input{}

	err := context.ShouldBindJSON(&input)
	if err != nil {
		context.JSON(http.StatusBadRequest, gin.H{"message": "枚举无效"})
		return
	}

	context.JSON(http.StatusOK, gin.H{"message": "正确的枚举"})
}

func ValidateEnum(fl validator.FieldLevel) bool {
	value := fl.Field().Interface().(Enum)
	return value.IsValid()
}

func main() {
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		v.RegisterValidation("enum", ValidateEnum)
	}

	router := gin.Default()

	router.POST("", UpdateRelationshipStatus)

	router.Run(":3000")
}

输出:

curl \
  --request POST \
  --data '{"relationship_status": 0}' \
  http://localhost:3000/
# {"message":"枚举无效"}

curl \
  --request POST \
  --data '{"relationship_status": 1}' \
  http://localhost:3000/
# {"message":"正确的枚举"}

curl \
  --request POST \
  --data '{"relationship_status": 2}' \
  http://localhost:3000/
# {"message":"正确的枚举"}

curl \
  --request POST \
  --data '{"relationship_status": 3}' \
  http://localhost:3000/
# {"message":"正确的枚举"}

curl \
  --request POST \
  --data '{"relationship_status": 4}' \
  http://localhost:3000/
# {"message":"枚举无效"}
英文:

One of the approach to this solution could be:

package main

import (
	"net/http"

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

type Enum interface {
	IsValid() bool
}

type Status int

const (
	Single Status = iota + 1 // add + 1 otherwise validation won't work for 0
	Married
	Other
)

func (s Status) IsValid() bool {
	switch s {
	case Single, Married, Other:
		return true
	}

	return false
}

type Input struct {
	RelationshipStatus Status `json:"relationship_status" binding:"required,enum"`
}

func UpdateRelationshipStatus(context *gin.Context) {
	input := Input{}

	err := context.ShouldBindJSON(&input)
	if err != nil {
		context.JSON(http.StatusBadRequest, gin.H{"message": "enum is not valid"})
		return
	}

	context.JSON(http.StatusOK, gin.H{"message": "correct enum"})
}

func ValidateEnum(fl validator.FieldLevel) bool {
	value := fl.Field().Interface().(Enum)
	return value.IsValid()
}

func main() {
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		v.RegisterValidation("enum", ValidateEnum)
	}

	router := gin.Default()

	router.POST("", UpdateRelationshipStatus)

	router.Run(":3000")
}

Output:

curl \
  --request POST \
  --data '{"relationship_status": 0}' \
  http://localhost:3000/
# {"message":"enum is not valid"}

curl \
  --request POST \
  --data '{"relationship_status": 1}' \
  http://localhost:3000/
# {"message":"correct enum"}

curl \
  --request POST \
  --data '{"relationship_status": 2}' \
  http://localhost:3000/
# {"message":"correct enum"}

curl \
  --request POST \
  --data '{"relationship_status": 3}' \
  http://localhost:3000/
# {"message":"correct enum"}

curl \
  --request POST \
  --data '{"relationship_status": 4}' \
  http://localhost:3000/
# {"message":"enum is not valid"}

答案2

得分: 1

同意mikolaj的答案。但为了避免这种方法带来的缺点,你可以尝试利用类似https://github.com/nishanths/exhaustive的工具,它可以在CICD系统中检查和报告错误,以防止你忘记在switch语句中处理新的const值。

英文:

Agree with mikolaj's answer. But to avoid the disadvantage which comes with this approach, you can try to leverage something like https://github.com/nishanths/exhaustive which checks and errors in CICD systems if you forget to handle the new const value in switch cases.

huangapple
  • 本文由 发表于 2022年2月27日 01:14:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/71278746.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定