123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- package valid
- import (
- "context"
- "reflect"
- "strings"
- )
- // ValidateStruct validates a struct by checking the specified struct fields against the corresponding validation rules.
- // Note that the struct being validated must be specified as a pointer to it. If the pointer is nil, it is considered valid.
- // Use Field() to specify struct fields that need to be validated. Each Field() call specifies a single field which
- // should be specified as a pointer to the field. A field can be associated with multiple rules.
- // For example,
- //
- // value := struct {
- // Name string
- // Value string
- // }{"name", "demo"}
- // err := validation.ValidateStruct(&value,
- // validation.Field(&a.Name, validation.Required),
- // validation.Field(&a.Value, validation.Required, validation.Length(5, 10)),
- // )
- // fmt.Println(err)
- // // Value: the length must be between 5 and 10.
- //
- // An error will be returned if validation fails.
- func ValidateStruct(structPtr interface{}, fields ...*FieldRules) error {
- return ValidateStructWithContext(nil, structPtr, fields...)
- }
- // ValidateStructWithContext validates a struct with the given context.
- // The only difference between ValidateStructWithContext and ValidateStruct is that the former will
- // validate struct fields with the provided context.
- // Please refer to ValidateStruct for the detailed instructions on how to use this function.
- func ValidateStructWithContext(ctx context.Context, structPtr interface{}, fields ...*FieldRules) error {
- value := reflect.ValueOf(structPtr)
- if value.Kind() != reflect.Ptr || !value.IsNil() && value.Elem().Kind() != reflect.Struct {
- // must be a pointer to a struct
- return NewInternalError(ErrStructPointer)
- }
- if value.IsNil() {
- // treat a nil struct pointer as valid
- return nil
- }
- value = value.Elem()
- errs := Errors{}
- for i, fr := range fields {
- fv := reflect.ValueOf(fr.fieldPtr)
- if fv.Kind() != reflect.Ptr {
- return NewInternalError(ErrFieldPointer(i))
- }
- ft := findStructField(value, fv)
- if ft == nil {
- return NewInternalError(ErrFieldNotFound(i))
- }
- var err error
- if ctx == nil {
- err = Validate(fv.Elem().Interface(), fr.rules...)
- } else {
- err = ValidateWithContext(ctx, fv.Elem().Interface(), fr.rules...)
- }
- if err != nil {
- if ie, ok := err.(InternalError); ok && ie.InternalError() != nil {
- return err
- }
- if ft.Anonymous {
- // merge errors from anonymous struct field
- if es, ok := err.(Errors); ok {
- for name, value := range es {
- errs[name] = value
- }
- continue
- }
- }
- errs[getErrorFieldName(ft)] = err
- }
- }
- if len(errs) > 0 {
- return errs
- }
- return nil
- }
- // FieldRules represents a rule set associated with a struct field.
- type FieldRules struct {
- fieldPtr interface{}
- rules []Rule
- }
- // Field specifies a struct field and the corresponding validation rules.
- // The struct field must be specified as a pointer to it.
- func Field(fieldPtr interface{}, rules ...Rule) *FieldRules {
- return &FieldRules{
- fieldPtr: fieldPtr,
- rules: rules,
- }
- }
- // findStructField looks for a field in the given struct.
- // The field being looked for should be a pointer to the actual struct field.
- // If found, the field info will be returned. Otherwise, nil will be returned.
- func findStructField(structValue reflect.Value, fieldValue reflect.Value) *reflect.StructField {
- ptr := fieldValue.Pointer()
- for i := structValue.NumField() - 1; i >= 0; i-- {
- sf := structValue.Type().Field(i)
- if ptr == structValue.Field(i).UnsafeAddr() {
- // do additional type comparison because it's possible that the address of
- // an embedded struct is the same as the first field of the embedded struct
- if sf.Type == fieldValue.Elem().Type() {
- return &sf
- }
- }
- if sf.Anonymous {
- // delve into anonymous struct to look for the field
- fi := structValue.Field(i)
- if sf.Type.Kind() == reflect.Ptr {
- fi = fi.Elem()
- }
- if fi.Kind() == reflect.Struct {
- if f := findStructField(fi, fieldValue); f != nil {
- return f
- }
- }
- }
- }
- return nil
- }
- // getErrorFieldName returns the name that should be used to represent the validation error of a struct field.
- func getErrorFieldName(f *reflect.StructField) string {
- if tag := f.Tag.Get(ErrorTag); tag != "" && tag != "-" {
- if cps := strings.SplitN(tag, ",", 2); cps[0] != "" {
- return cps[0]
- }
- }
- return f.Name
- }
|