Last active
June 22, 2024 02:51
-
-
Save shaunlee/36386d2b9e7633b67410ff78a48f1964 to your computer and use it in GitHub Desktop.
Form validation with custom errors and nested structs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package forms | |
import ( | |
"github.com/go-playground/validator/v10" | |
"github.com/gofiber/fiber/v2" | |
"reflect" | |
"strings" | |
) | |
/* | |
type LoginMetadata struct { | |
OS string `form:"os" json:"os" validate:"required" errors.required:"OS is required"` | |
Version string `form:"version" json:"version" validate:"required" errors.required:"Version is required"` | |
} | |
type LoginForm struct { | |
Username string `form:"username" validate:"required" errors.required:"Username is required"` | |
Password string `form:"password" validate:"required" errors.required:"Passsword is required"` | |
Metadata LoginMetadata `form:"metadata" json:"metadata"` | |
} | |
*/ | |
type ValidationError fiber.Map | |
func (e ValidationError) Error() string { | |
return "ValidationError: Unprocessable Content" | |
} | |
var validate = validator.New() | |
func BindAndValidate(c *fiber.Ctx, v interface{}) error { | |
if err := c.BodyParser(v); err != nil { | |
return ValidationError{ | |
"default": err.Error(), | |
} | |
} | |
if err := validate.Struct(v); err != nil { | |
errs := ValidationError{} | |
ref := reflect.TypeOf(v).Elem() | |
for _, e := range err.(validator.ValidationErrors) { | |
if field, ok := ref.FieldByName(e.Field()); ok { | |
k, v := getErrorMessage(field, e) | |
errs[k] = v | |
} else { | |
subLevel(ref, e, errs, 1) | |
} | |
} | |
return errs | |
} | |
return nil | |
} | |
func subLevel(ref reflect.Type, e validator.FieldError, errs ValidationError, level int) { | |
if pf, ok := ref.FieldByName(strings.SplitN(e.Namespace(), ".", -1)[level]); ok { | |
k := pf.Tag.Get("form") | |
if len(k) == 0 { | |
k = pf.Tag.Get("json") | |
if len(k) == 0 { | |
k = pf.Name | |
} | |
} | |
if _, ok := errs[k]; !ok { | |
errs[k] = ValidationError{} | |
} | |
if cf, ok := pf.Type.FieldByName(e.Field()); ok { | |
ck, cv := getErrorMessage(cf, e) | |
if kv, ok := errs[k].(ValidationError); ok { | |
kv[ck] = cv | |
} | |
} else if kv, ok := errs[k].(ValidationError); ok { | |
subLevel(pf.Type, e, kv, level+1) | |
} | |
} | |
} | |
func getErrorMessage(field reflect.StructField, e validator.FieldError) (string, string) { | |
k := field.Tag.Get("form") | |
if len(k) == 0 { | |
k = field.Tag.Get("json") | |
if len(k) == 0 { | |
k = e.Field() | |
} | |
} | |
v := field.Tag.Get("errors." + e.ActualTag()) | |
if len(v) == 0 { | |
v = e.Error() | |
} | |
return k, v | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment