Vaishak
Vaishak

Reputation: 97

Request body validation with http Mux

I am trying to build an API using Go and Mux. I am trying to validate the incoming requests. I tried using tags on Struct and validated it using Go Validator.

This is my Struct

type Address struct {
    Street string `validate:"required"`
    City   string `validate:"required"`
    Phone  string `validate:"required"`
}

My issue is

eg. API might receive

{
  "Street": "Dummy"
}

What is the best way to validate both POST and PATCH request in this scenario.

Upvotes: 1

Views: 3814

Answers (3)

Fahim Bagar
Fahim Bagar

Reputation: 828

Change my answer

I've tested on your case and found the solution, you must use struct level validation. Here is the my function on your case:

func PostRequestValidation(sl validator.StructLevel) {

    address := sl.Current().Interface().(Address)

    jsonMarshal, _ := json.Marshal(address)
    var m map[string]interface{}
    _ = json.Unmarshal(jsonMarshal, &m)
    for key, val := range m {
        if len(fmt.Sprintf("%s", val)) == 0 {
            sl.ReportError(val, key, key, "post", "")
        }
    }
}

func PutRequestValidation(sl validator.StructLevel) {

    address := sl.Current().Interface().(Address)

    isValid := false

    jsonMarshal, _ := json.Marshal(address)
    var m map[string]interface{}
    _ = json.Unmarshal(jsonMarshal, &m)
    for _, val := range m {
        if len(fmt.Sprintf("%s", val)) > 0 {
            isValid = true
        }
    }

    if !isValid {
        sl.ReportError(address, "address", "Adress", "put", "")
    }
}

You just have to register on each validation request

    // ON POST REQUEST
    validate = validator.New()
    validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
        name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
        if name == "-" {
            return ""
        }
        return name
    })
    validate.RegisterStructValidation(PostRequestValidation, Address{})
    // ON PUT REQUEST
    validate = validator.New()
    validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
        name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
        if name == "-" {
            return ""
        }
        return name
    })
    validate.RegisterStructValidation(PutRequestValidation, Address{})

You may copy from my source code for more detailed source code.

Upvotes: 2

Vaishak
Vaishak

Reputation: 97

Found this library - govalidator. This allows to create rules for each request based on the requirement. For POST request, create a set of rules with all the fields as mandatory. For PATCH request, create rules with all fields as optional.

Upvotes: 0

Peter
Peter

Reputation: 31701

In the PATCH endpoint use a different type with different (if any) validation tags. Convert to Address afterward:

// Same underlying type as Address, but no validation tags
var x struct {
    Street string
    City   string
    Phone  string
}

// Decode request into x, then:

addr := Address(x)

Upvotes: 2

Related Questions