Reputation: 97
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
POST
endpoint where all the fields would be passed and the validation would not fail. However there is another PATCH
endpoint where not all fields of Address
struct would be passed and hence the validation is failing.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
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
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
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