2

I am using go Gin to create APIs in my project. I have requirement to create custom validators so I created like:

var valueone validator.Func = func(fl validator.FieldLevel) bool {
    value, ok := fl.Field()
    if ok {
        if value != "one" {
                  return true
                }
    }
    return true
}

var valuetwo validator.Func = func(fl validator.FieldLevel) bool {
    value, ok := fl.Field()
    if ok {
        if value != "two" {
                  return true
                }
    }
    return true
}
....

Instead of creating almost same custom validator multiple times, is there a way to create a single validator which is more generic and can be used for both cases, something like:

var value validator.Func = func(fl validator.FieldLevel, param) bool {
    value, ok := fl.Field()
    if ok {
        if value != param {
                  return true
                }
    }
    return true
}

I am not able to find a way to pass parameter to custom validator in gin or to make these generic validators through any other possible way. I have requirement to create like thousands of almost similar validator and I don't want to create custom validator for each one of them.

Hemant Kumar
  • 161
  • 7

2 Answers2

2

You can't change the function structure, as this is how it defined within the package.

// Func accepts a FieldLevel interface for all validation needs. The return
// value should be true when validation succeeds.

type Func func(fl FieldLevel) bool

instead, we could try custom validation tag with a parameter

see the sample below

package main

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

type Data struct {
    One string `json:"one" validate:"custom_validation=one"`
    Two string `json:"two" validate:"custom_validation=two"`
}

var validate *validator.Validate

func main() {
    validate = validator.New()

    err := validate.RegisterValidation("custom_validation", func(fl validator.FieldLevel) bool {
        value := fl.Field()
        param := fl.Param()

        return value.String() == param
    })

    if err != nil {
        panic(err)
    }

    // this will succeed
    {
        data := &Data{
            One: "one",
            Two: "two",
        }

        err = validate.Struct(data)
        if err != nil {
            panic(err)
        }
    }

    // it will fail here
    {
        data := &Data{
            Two: "one",
            One: "two",
        }

        err = validate.Struct(data)
        if err != nil {
            panic(err)
        }
    }
}

See more examples at here

Note : Golang doesn't support !==

PRATHEESH PC
  • 1,461
  • 1
  • 3
  • 15
2

In go-playground/validator, which Gin uses for validation, you can define your custom validator to use a parameter, and you can include the parameter in the validation tag in your struct.

Here's how you can do it:

First, register a function with the tag name equals. The second parameter in the function is the tag's parameter, which will be the value you want to check for equality against:

validate := validator.New()
_ = validate.RegisterValidation("equals", func(fl validator.FieldLevel) bool {
    param := fl.Param()
    return fl.Field().String() == param
})

Then, in your struct, you can use the equals tag followed by = and the value:

type MyStruct struct {
    FieldOne string `validate:"equals=one"`
    FieldTwo string `validate:"equals=two"`
}

In this case, FieldOne must equal "one" and FieldTwo must equal "two".

So, with this approach, you only have to define your custom validation function once, and then you can reuse it with different parameters for different fields.

Please note that fl.Param() returns the parameter as a string. If your parameters are not strings, you might need to parse them to their actual type inside the validation function. Also, ensure to do error handling for cases when validator.New() and validate.RegisterValidation() might return an error.