Search code examples
validationgogo-gin

How to get dynamic type of empty interface


I tried to test validation of request data in go-gin framework so far I reached this code that work fine.

// RegisterEmailPasswordValidator it is used for validation and json marshalling
type RegisterEmailPasswordValidator struct {
    Email    string `json:"email" binding:"required,email"`
    Password string `json:"password" binding:"required,gte=6,lte=30"`
}

// Validate used in views
func (v *RegisterEmailPasswordValidator) Validate( c *gin.Context) error {
    if err := c.ShouldBindJSON(v); err != nil {
        return err
    }
    return nil
}

// use it like this in my controller
var validator validations.RegisterEmailPasswordValidator
err := validator.Validate(c)

To be DRY, I tried to add a global function to Validate responsibility for all structs. I tried to add a function with empty interface like bellow.

// Validate used in views
func Validate(c *gin.Context, customValidator interface{}) error {   
    if err := c.ShouldBindJSON(&customValidator); err != nil {
        return err
    }
    return nil
}

// use it like this in my controller
var validator validations.RegisterEmailPasswordValidator
err := validations.Validate(c, &validator)

But it's not working when I send customValidator to ShouldBindJSON It uses empty interface not it's dynamic type.

Is there any way to use dynamic type of customValidator without mentioning it's type? I tried reflect.
Is there better way to be DRY in this situation in go?


Solution

  • You should pass the interface as is, and not the pointer to the interface. something like this :

    // Validate used in views
    func Validate(c *gin.Context, customValidator interface{}) error {   
        if err := c.ShouldBindJSON(customValidator); err != nil {
            return err
        }
        return nil
    }
    
    // use it like this in my controller
    var validator validations.RegisterEmailPasswordValidator
    err := validations.Validate(c, &validator)
    

    The reason is pointers in Go are not the same as pointers in C/C++, they keep the type and the value reference. A pointer to an interface is not a pointer to type it holds, but it's a pointer to the interface itself, the type is an interface. In ShouldBindJSON the function tries to get the object by reflection, but it gets the interface type and reference, not the validators reference, therefore it can not do its job, since it needs a type, not an interface.