Home > database >  Using validator to check if value is boolean
Using validator to check if value is boolean

Time:08-11

I'm new to Go, so this might be very easy, but I can't find it. I have an entity Page with these two values:

type Page struct {
    Title   string `form:"title" binding:"required"`
    Active  bool
}

Now, if you don't send a title we get a (not very pretty, but acceptable*):
Key: 'Page.Title' Error:Field validation for 'Title' failed on the 'required' tag.

Now, if I send this to the endpoint:

{
    "title": "I'm a valid title",
    "active": "I'm not a boolean at all!"
}

We get this:
json: cannot unmarshal string into Go struct field Page.Active of type bool

Which, IMO, is giving way too much info. What is the standard practice in Go to validate user input?

I was first making a page-validor.go with some checks, then I found this, but I'm not sure what is good practice in Go.
How do I validate this properly? Should I find check what is provided and then try to move it into the struct and validate the actual contents?

I am using GinGonic
* I've found a way to unwrap the errors and make it nicer

CodePudding user response:

Write custom JSON Unmarshaller method for the type Page and inside UnmarshalJSON(bytes []byte) method, you can unmarshal the JSON bytes to map[string]interface{} and then validate the types you need with the JSON field keys.

An example of the JSON Unmarshaller looks like below.

type Page struct {
    Title  string `form:"title" binding:"required"`
    Active bool
}

func (p *Page) UnmarshalJSON(bytes []byte) error {
    var data map[string]interface{}
    err := json.Unmarshal(bytes, &data)
    if err != nil {
        return err
    }
    actv, _ := data["active"]
    if reflect.TypeOf(actv).Kind() != reflect.Bool {
        return errors.New("active field should be a boolean")
    }

    p.Active = actv.(bool)

    return nil
}

See the full example here in Playground.

CodePudding user response:

After some more research, I've implemented Go-map-schema.

var page Page
src := make(map[string]interface{})
json.Unmarshal(jsonData, &src)

results, _ := schema.CompareMapToStruct(page, src, nil)

fmt.Println(results.MissingFields)
fmt.Println(results.MismatchedFields)

This works simple with the standard notations for an struct:

type Page struct {
    Title  string `json:"title" validator:"required"`
    Active bool   `json:"metaRobotsFollow" validator:"required,bool"`
}

CodePudding user response:

You should use validator v10 available with GinGonic

validator documentation go

For your use case you can use boolean validator
https://pkg.go.dev/github.com/go-playground/validator/v10#hdr-Boolean

type Page struct {
  Title  string `json:"title" binding:"required"`
  Active bool   `json:"active" binding:"required,boolean"`
}

Below is sample Test case with one positive and negative

func TestPage(t *testing.T) {

tests := []struct {
    testName string
    input    string
    wantErr  bool
}{
    {
        testName: "positive test",
        input: `{
                 "title": "first book title",
                  "active": false
                }`,
        wantErr: false,
    }, {
        testName: "wrong boolean",
        input: `{
                 "title": "second book title",
                  "active": falsee
                }`,
        wantErr: false,
    },
}

for _, tt := range tests {
    t.Run(tt.input, func(t *testing.T) {
        var p Page
        b := []byte(tt.input)
        err := json.Unmarshal(b, &p)
        assert.Nil(t, err, "got error %v", err)
    })
}
  • Related