- With issues:
- Use the search tool before opening a new issue. - done
- Please provide source code and commit sha if you found a bug. - done
- Review existing issues and provide feedback or react to them. - Done
Description
I am trying to get the validation working in gin, with the validator package this validator tag works good. When i use it in gin it's not working and i am not sure if i am using it wrong or it's not working as a bug. What all i did before opening this issue 1. Took just the validator lib and did a small test case to make sure validator and tags are good 2. Google or issue search does not show this validate tag being used and errors on it 3. The looked at gin code to see that validation is triggered but not working in my case
I looked at the gin code and see here that ShouldBindWith()
should eventually call this .
https://github.com/gin-gonic/gin/blob/4b68a5f12af4d6d2be83e1895f783d5dd5d5a148/binding/query.go
Once validate() is called it should validate using the validator package for the validate tag, which is not working as expected
validate:"omitempty,oneof=ASC DESC
Any help is appreciated on this
How to reproduce
package main
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
// MyStruct ..
type MyStruct struct {
Order string `form:"order,default=DESC" validate:"omitempty,oneof=ASC DESC"`
}
func startPage(c *gin.Context) {
var person MyStruct
if c.ShouldBindWith(&person, binding.Query) == nil {
log.Println("====== Only Bind By Query String ======")
log.Println(person.Order)
c.String(http.StatusOK, "Success")
return
// log.Println(person.Address)
}
c.String(http.StatusBadRequest, "Failed")
return
}
func main() {
route := gin.Default()
route.Any("/testing", startPage)
route.Run(":8085")
// This is just to show you guys that validate package works well throwing errors
// validate = validator.New()
// validate.RegisterValidation("method", ValidateMyVal)
// s := MyStruct{Order: "awesome"}
// err := validate.Struct(s)
// if err != nil {
// fmt.Printf("Err(s):\n%+v\n", err)
// } else {
// fmt.Println("awesome validation looks good ")
// }
// s.Order = "not awesome"
// err = validate.Struct(s)
// if err != nil {
// fmt.Printf("Err(s):\n%+v\n", err)
// }
// s.Order = "awesome mess"
// err = validate.Struct(s)
// if err != nil {
// fmt.Printf("Err(s):\n%+v\n", err)
// } else {
// fmt.Println("mess validation looks good ")
// }
}
Expectations
Here i expect the curl command to return me a StatusBadRequest and text "failed"
$ curl -G -d 'order=WRONG' http://localhost:8085/testing
Failed
Actual result
Actual result is code StatusOk and "Success"
√ go_learning/learngo % curl -G -d 'order=WRONG' http://localhost:8085/testing
Success
Environment
- go version: go1.17.6 darwin/amd64
- gin version (or commit ref): github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-gonic/gin v1.8.1 // indirect
- operating system: Mac OS - Big Sur 11.6.7
Comment From: eleven26
You should use binding
, not validate
:
binding:"omitempty,oneof=ASC DESC"
Because gin has modified the validator's tagname : default_validator.go#L95
func (v *defaultValidator) lazyinit() {
v.once.Do(func() {
v.validate = validator.New()
v.validate.SetTagName("binding")
})
}
Comment From: rahmathu
@eleven26 : Thanks for the replies , some more questions to this
If i use binding
in place of validate
the "omitempty" does not work as it tries to unmarshall and expects it to be there. Do you know how may i do an omitempty
and then oneof in the same tag. In case it will be a custom validator please give me an example with this sample code as i have tried may options but it's not wokring for me . Thanks in advance
Comment From: eleven26
The omitempty
in binding
just means that if your request does not have this field in it, it will not check other validation rules.
Comment From: rahmathu
@eleven26 : Thanks for the confirmation, let me try out and get back. Till then please leave the issue open, i expect to close on this by this Friday . I will close the issue with comments my self if all goes well
Comment From: rahmathu
@eleven26 : Thanks all the things worked except on last thing on the validator for dates
type GeneralValidatorQueryParams struct {
StartedAt time.Time `form:"startedAt" binding:"omitempty,required_with=EndedAt"`
EndedAt time.Time `form:"endedAt" binding:"omitempty,required_with=StartedAt,gtfield=StartedAt"`
}
In this case i was trying to use this for a GET request and these are query parameters.
1. When i put query parameter as in the URL "/testGeneralInputValidator?endedAt=2009-01-06T12:59:59Z". I see that the validator is not showing an error when i have missed the startedAt
field.
2. I have also tried the reverse for "/testGeneralInputValidator?startedAt=2009-01-10T12:59:59Z" . I see that the validation does not fail for this query parameter. I am not sure why the tag required_with
is not taking effect here with binding .
Comment From: eleven26
- You should not use
omitempty
, because you already haverequired_with
, which means you only checkStartedAt
when you passEndedAt
parameter, otherwise,validator
will not check other validation rules when it seesomitempty
:
type GeneralValidatorQueryParams struct {
StartedAt time.Time `form:"startedAt" binding:"required_with=EndedAt"`
EndedAt time.Time `form:"endedAt" binding:"omitempty,required_with=StartedAt,gtfield=StartedAt"`
}
This may be a correct example.
- Same as before. If you use
required_with
you can't useomitempty
anymore, if you addomitempty
and the field doesn't existrequired_with
and other validation rules no longer work
You may not fully understand the usage of omitempty
. The following is the description of omitempty in the official documentation:
validator.v9#hdr-Omit_Empty Allows conditional validation, for example if a field is not set with a value (Determined by the "required" validator) then other validation such as min or max won't run, but if a value is set validation will run.
Comment From: rahmathu
got it thanks, i will close the issue now :)