type Req struct {
Field int `json:"field" binding:"required"`
}
func handler(ctx *gin.Context) {
req := &Req{}
err := ctx.BindJSON(&req)
}
"Key: 'Req.Field' Error:Field validation for 'Field' failed on the 'required' tag"
So how to avoid the error if I need to parse {"field":0}
?
Comment From: deankarn
duplicate, this has been answered many times: https://github.com/gin-gonic/gin/issues/690 https://github.com/gin-gonic/gin/issues/659 https://github.com/gin-gonic/gin/issues/611 https://github.com/gin-gonic/gin/issues/491
in short the validation is run after the data is unmarshalled into the struct and so even though Field
in your example was not posted because of Go's static nature there will still be a default value and required
checks that it's not the default value....the way to do this is to make int
a pointer *int
that way the default value is nil
vs 0
and so when the value is not posted required
will work as you expect.
Comment From: rstrlcpy
Thanks. It would be nice if someone added several notes about this to documentation.
Comment From: appleboy
Answer:
type Req struct {
- Field int `json:"field" binding:"required"`
+ Field *int `json:"field" binding:"exists"`
}
Comment From: elSuperRiton
I've tried switching to pointers value for int + changing my bindings to exists but somehow the BindWithJSON function still crashes when binding from JSON with a request where values of type *int (or int) start with 0
.
I just can't wrap my head around how to solve this.
My structs look like this :
type User struct {
gorm.Model
Firstname string `json:"firstname" binding:"required"`
Lastname string `json:"lastname" binding:"required"`
Email string `json:"email" binding:"required" gorm:"unique"`
Password string `json:"password" binding:"required"`
Phones Phones `gorm:"-" json:"phones" binding:"gt=0,dive"`
Addresses Addresses `gorm:"-" json:"addresses" binding:"gt=0,dive"`
}
type Phone struct {
gorm.Model
UserID uint64
PhonePOID *int `json:"phone_poid" binding:"exists, numeric"`
PhoneNumber *int `json:"phone_number" binding:"exists, numeric"`
Main bool `json:"main"`
}
Comment From: decipherpunk
I create a simple to debug my actual application which is hitting a blocker:
Here is my form :
package form
type TestForm struct {
Age int `form:age" json:"age" binding:"exists"`
FirstName string `form:"firstname" json:"firstname" binding:"required"`
LastName string `form:"lastname" json:"lastname" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
}
HTML Form
<form role="form" id="config" method="post" action="/v1/api/config/test/set">
<div class="form-group">
<label for="age">age</label>
<input type="number" name="age" class="form-control" id="age" readonly>
</div>
<div class="form-group">
<label class="required-pf" for="firstname">firstname</label>
<input type="text" name="firstname" class="form-control" id="firstname" required>
</div>
<div class="form-group">
<label class="required-pf" for="lastname">lastname</label>
<input type="text" name="lastname" class="form-control" id="lastname" required>
</div>
<div class="form-group">
<label class="required-pf" for="password">Password</label>
<input type="password" name="password" class="form-control" id="password" required>
</div>
<button type="submit" class="btn btn-default">Save Configuration</button>
</form>
Controller
func (ctrl TestController) TestConfig(c *gin.Context){
var testForm form.TestForm
if c.ShouldBindWith(&testForm, binding.FormPost) != nil {
c.Redirect(302, "/v1/console/config/request_failed")
}
TestModel.TestConfig(testForm)
fmt.Println(testForm.age)
c.Redirect(302, "/v1/console/config/success")
}
And a model test save into a json file
func (m TestModel) ServerConfig(form form.TestForm) (){
jsonFile, err := os.Open("config/dev.json")
if err != nil {
fmt.Println(err)
}
defer jsonFile.Close()
data, _ := ioutil.ReadAll(jsonFile)
age, _ := sjson.Set(string(data), "app.age", form.Age)
fmt.Println(form.age) // returns 0
firstName, _ := sjson.Set(age, "app.firstname", form.FirstName)
lastName, _ := sjson.Set(firstName, "app.lastname", form.LastName)
secret, _ := sjson.Set(lastName, "app.secret", form.Secret)
err = ioutil.WriteFile("config/dev.json", []byte(secret), 0644)
}
Not sure what's happening but form always ends up providing 0 for age regardless of the actual input.
I am seriously lost, not sure what i am doing wrong all the form fields work except the one with int.
Comment From: LaysDragon
@deankarn I can understand why we need use pointer with binding required validator because of golang's nature, but it is really counterintuitive for most people read the quick start from the gin README.md tutorial,it make me expected it only validating on the source data and failed while json missed the field. But it not,it actually working on the unmarshalled struct make it have it's limit(it lost the raw data struct while them unmarshalled into a struct and those lost field turn into zero value and ambiguity with real value).
I feeling it is dangerous while people write code with what they really expected from readme's example because of the json/xml/anything-else's dynamic compare to golang static stuct. It might be better if this behavior can mention on the readme about how required tag working.
Meanwhile,I thought that validator require to change the original data struct to make work is strange,isn't that golang's struct meta tag coming for slove? Since the validator give me the an expected that it is is extra functions to validate on source data(whenin gin),the requirement of pointer break the data struct's design and struct tag's intention..
Like I using the openapi generator.the pointer is decide via the field's attr,if I hope to to share code with go gin,I have to rewrite the generator's template to make every field a pointer,that really not a good solution
Other situation is use with other framework like gorm,I don't really want to make everything into pointer just because of gin validator's limit,or need to matain the same copy struct from the share one. Hope if there any workaround to made gin validator working on source data(json/xml/...) instead of the unmarshalled struct while I use the gin framework for such situation.
Comment From: deankarn
@LaysDragon please don't post on closed issues, create a new one and reference. 😄
As for the Gin documentation, will leave that to the Gin authors. As for validator there is no way besides: - Having pointer - Using a nullable type eg. sql.NullString and those types can be used with validator if register just like here
For context validator
on purpose does not validate the source data but struct and field data so that it doesn't matter where the source data came from eg. manually created, Form Post, Database......
I'm sure there are other way to validate the source data but are outside the use case of validator
The reality is to capture that undefined/nil/null state you have to account for it using a pointer, custom type or interface{] which is arguably still a pointer.
Comment From: toudi
I just stumbled upon this issue today and the highlighted response suggested using exists
validator, however this validation function no longer exists (oh, the irony :)). Anyhow, the short answer is, as somebody pointed out already is to use a pointer with required
validator, like so:
type MyModel struct {
MyField *int `json:"my_field" binding:"required"`
}
func MyFunction(c *gin.Context) {
var myModel MyModel;
err := c.shouldBind(&myModel);
// the rest of the logic
// - if you do want to use the int value, you have to de-reference it, like so:
var myIntValue = *myModel.MyField
}
Comment From: nuffin
duplicate, this has been answered many times: #690 #659 #611 #491
in short the validation is run after the data is unmarshalled into the struct and so even though
Field
in your example was not posted because of Go's static nature there will still be a default value andrequired
checks that it's not the default value....the way to do this is to makeint
a pointer*int
that way the default value isnil
vs0
and so when the value is not postedrequired
will work as you expect.
asked many times, but still "works". it's fine
I'm using pointer too, and sometimes failed with "required" binding, not every try with the same request.
Comment From: nuffin
I just stumbled upon this issue today and the highlighted response suggested using
exists
validator, however this validation function no longer exists (oh, the irony :)). Anyhow, the short answer is, as somebody pointed out already is to use a pointer withrequired
validator, like so:
``go type MyModel struct { MyField *int
json:"my_field" binding:"required"` }func MyFunction(c *gin.Context) { var myModel MyModel; err := c.shouldBind(&myModel);
// the rest of the logic // - if you do want to use the int value, you have to de-reference it, like so: var myIntValue = *myModel.MyField
} ```
exists does not exist :D
Comment From: MrNocTV
Answer:
diff type Req struct { - Field int `json:"field" binding:"required"` + Field *int `json:"field" binding:"exists"` }
It should be required
Comment From: danielalexis
"exists" no longer exists on the package gopkg.in/go-playground/validator
so it doesn't work anymore.
https://pkg.go.dev/github.com/go-playground/validator/v10@v10.16.0
If you are working with positive integers I would recommend gte=0
Comment From: piti118
as @MrNocTV mentioned PSA: for those of you who got to this issue in 2024 and beyond. exists is no longer supported in validator. It should be
diff type Req struct { - Field int `json:"field" binding:"required"` + Field *int `json:"field" binding:"required"` }
Comment From: mrcleanandfresh
I was experiencing an issue with boolean values; without the fix, the validator was saying that my boolean wasn't present. I had to use:
type Req struct {
Field *bool `json:"field" binding:"required"`
}
This may be a Golang 101 question, so bear with me: When this request comes into Gin, via the Request.Body
which I'm assuming is like a byte stream that needs to be opened, read, and mapped, when doing c.BindJSON
. In that byte stream (or whatever is happening), are the booleans represented by binary 0
or 1
values that must be mapped to false
or true
depending on the type of the struct?
Why is it necessary to throw that pointer on there to make it work?