Validate enum in Golang using Gin framework
Asked Answered
R

3

6

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() {}
Redware answered 26/2, 2022 at 17:14 Comment(2)
Do you want to check if variables with the given name are used or just check if argument is within range?Repeal
Check if argument within range.Redware
R
4

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"}
Redware answered 26/2, 2022 at 18:58 Comment(3)
But disadvantage of this approach is hardcoded value which we have to change manually every time enum value has changed. isn't this exactly what you do not want?Repeal
So far I didn't find a better way that's way I posted this solution, but if you post finer answer I would be glad to accept it.Redware
But first you said: Is there any way to get enum by its name as string - how is this requirement covered in your answer?Repeal
E
1

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.

Erotica answered 31/5, 2023 at 15:35 Comment(0)
I
1

This feature is already implemented in go-playground/validator:

https://pkg.go.dev/github.com/go-playground/validator/v10#hdr-One_Of

Just use smth like validate:"required,oneof=active inactive" for string enums

Inordinate answered 21/11, 2023 at 11:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.