The following test should pass, but actually it fails.

func TestValidationSuccess(t *testing.T) {
    type HogeStruct struct {
        Hoge int `json:"hoge" binding:"required"`
    }

    var obj HogeStruct
    req := requestWithBody("POST", "/", `{"hoge": 0}`)
    err := JSON.Bind(req, &obj)
    assert.NoError(t, err)
}

The reason is a validator problem. See more detail hasValue in gopkg.in/bluesuncorp/validator.v5/baked_in.go

In the function, the validation checks with the following line

        return field != nil && field != reflect.Zero(reflect.TypeOf(field)).Interface()

But this is wrong. Because whenever "field == 0", it always fails. Actually if "field != 0", it succeeds. (In my previous test, {"hoge":1} succeeds.)

There are a couple of approaches. 1. Ask bluesuncorp/validator to fix the problem 2. Not to use bluesuncorp/validator and fix the problem by ourselves.

What do you think? I wonder most of the people don't use Bind() function and parse JSON by themselves?

Comment From: deankarn

Hello @honteng,

this is not a problem with validator, I have addressed this before in this issue https://github.com/go-playground/validator/issues/142 with the "exists" tag.

To summarize Go is a static language so when the struct "HogeStruct" is initialized in order to set the JSON values the value of the "Hoge" field will get initialized to "0", the "required" tag does not ensure a field exists, just that it is not the default value.

The only way to do what you want is by using the "exists" tag like so:

func TestValidationSuccess(t *testing.T) {
    type HogeStruct struct {
        Hoge *int `json:"hoge" binding:"exists"`
    }

    var obj HogeStruct
    req := requestWithBody("POST", "/", `{"hoge": 0}`)
    err := JSON.Bind(req, &obj)
    assert.NoError(t, err)
}

So by changing the field value to *int when the field "Hoge" is not posted down it will be nil and will get caught by the "exists" tag. I wish there were a better way to do it, but because of Go's static nature it is the only way to catch if a value exists.

P.S. I really wish Gin would update to v8 of validator as it has many more validations and much better performance, but it would be a breaking change; you can Unmarshall the JSON and validate without using Bind() if you wish.

Comment From: honteng

Than you for response.

So far, I can use your workaround for our purpose. I didn't know the "exists" tag.

But it could be great to see some tests. Do you want me to write?

Comment From: deankarn

Sure, I'm all for any help

Comment From: honteng

Ok, now I have some time to work on the tests. https://github.com/gin-gonic/gin/pull/504 Please review it.

Thanks,

Comment From: deankarn

@honteng it LGTM but one of the repository admins will have to merge, sorry if I gave the impression I was one.

Comment From: honteng

So just waiting for the admin person? Or should I do an extra action?

Comment From: deankarn

I'd just wait, it may be a while though as the maintainers of gin are a little busy with school

Comment From: javierprovecho

Hello @honteng,

Yes, as @joeybloggs pointed, we are busy with university, more even now that we have exams. I expect to have more time by the firsts days of February.

Thank you for understanding.

Comment From: honteng

no problem :-)

Comment From: thinkerou

closing

Comment From: mohsenasm

Since there is no exists tag anymore, we should use *int if we want to allow zero-value. See this issue.

Comment From: kumarvimal

I was able to solve a similar issue using number tag

Comment From: duktig-dev

Yes, the issue exists when trying to validate value with "0" (which are allowed to set). However, I'm using the approach as listed above:

type UserCreateValidation struct {
    Status        int    `json:"status" binding:"required,numeric"`
    EmailVerified *int   `json:"email_verified" binding:"required,number,lte=1,gte=0"`
        // Other values here ...
}

So with gt or gte validation rules it's handling correct. Thanks!