github.com/astaxie/beego@v1.12.3/validation/validation.go (about)

     1  // Copyright 2014 beego Author. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package validation for validations
    16  //
    17  //	import (
    18  //		"github.com/astaxie/beego/validation"
    19  //		"log"
    20  //	)
    21  //
    22  //	type User struct {
    23  //		Name string
    24  //		Age int
    25  //	}
    26  //
    27  //	func main() {
    28  //		u := User{"man", 40}
    29  //		valid := validation.Validation{}
    30  //		valid.Required(u.Name, "name")
    31  //		valid.MaxSize(u.Name, 15, "nameMax")
    32  //		valid.Range(u.Age, 0, 140, "age")
    33  //		if valid.HasErrors() {
    34  //			// validation does not pass
    35  //			// print invalid message
    36  //			for _, err := range valid.Errors {
    37  //				log.Println(err.Key, err.Message)
    38  //			}
    39  //		}
    40  //		// or use like this
    41  //		if v := valid.Max(u.Age, 140, "ageMax"); !v.Ok {
    42  //			log.Println(v.Error.Key, v.Error.Message)
    43  //		}
    44  //	}
    45  //
    46  // more info: http://beego.me/docs/mvc/controller/validation.md
    47  package validation
    48  
    49  import (
    50  	"fmt"
    51  	"reflect"
    52  	"regexp"
    53  	"strings"
    54  )
    55  
    56  // ValidFormer valid interface
    57  type ValidFormer interface {
    58  	Valid(*Validation)
    59  }
    60  
    61  // Error show the error
    62  type Error struct {
    63  	Message, Key, Name, Field, Tmpl string
    64  	Value                           interface{}
    65  	LimitValue                      interface{}
    66  }
    67  
    68  // String Returns the Message.
    69  func (e *Error) String() string {
    70  	if e == nil {
    71  		return ""
    72  	}
    73  	return e.Message
    74  }
    75  
    76  // Implement Error interface.
    77  // Return e.String()
    78  func (e *Error) Error() string { return e.String() }
    79  
    80  // Result is returned from every validation method.
    81  // It provides an indication of success, and a pointer to the Error (if any).
    82  type Result struct {
    83  	Error *Error
    84  	Ok    bool
    85  }
    86  
    87  // Key Get Result by given key string.
    88  func (r *Result) Key(key string) *Result {
    89  	if r.Error != nil {
    90  		r.Error.Key = key
    91  	}
    92  	return r
    93  }
    94  
    95  // Message Set Result message by string or format string with args
    96  func (r *Result) Message(message string, args ...interface{}) *Result {
    97  	if r.Error != nil {
    98  		if len(args) == 0 {
    99  			r.Error.Message = message
   100  		} else {
   101  			r.Error.Message = fmt.Sprintf(message, args...)
   102  		}
   103  	}
   104  	return r
   105  }
   106  
   107  // A Validation context manages data validation and error messages.
   108  type Validation struct {
   109  	// if this field set true, in struct tag valid
   110  	// if the struct field vale is empty
   111  	// it will skip those valid functions, see CanSkipFuncs
   112  	RequiredFirst bool
   113  
   114  	Errors    []*Error
   115  	ErrorsMap map[string][]*Error
   116  }
   117  
   118  // Clear Clean all ValidationError.
   119  func (v *Validation) Clear() {
   120  	v.Errors = []*Error{}
   121  	v.ErrorsMap = nil
   122  }
   123  
   124  // HasErrors Has ValidationError nor not.
   125  func (v *Validation) HasErrors() bool {
   126  	return len(v.Errors) > 0
   127  }
   128  
   129  // ErrorMap Return the errors mapped by key.
   130  // If there are multiple validation errors associated with a single key, the
   131  // first one "wins".  (Typically the first validation will be the more basic).
   132  func (v *Validation) ErrorMap() map[string][]*Error {
   133  	return v.ErrorsMap
   134  }
   135  
   136  // Error Add an error to the validation context.
   137  func (v *Validation) Error(message string, args ...interface{}) *Result {
   138  	result := (&Result{
   139  		Ok:    false,
   140  		Error: &Error{},
   141  	}).Message(message, args...)
   142  	v.Errors = append(v.Errors, result.Error)
   143  	return result
   144  }
   145  
   146  // Required Test that the argument is non-nil and non-empty (if string or list)
   147  func (v *Validation) Required(obj interface{}, key string) *Result {
   148  	return v.apply(Required{key}, obj)
   149  }
   150  
   151  // Min Test that the obj is greater than min if obj's type is int
   152  func (v *Validation) Min(obj interface{}, min int, key string) *Result {
   153  	return v.apply(Min{min, key}, obj)
   154  }
   155  
   156  // Max Test that the obj is less than max if obj's type is int
   157  func (v *Validation) Max(obj interface{}, max int, key string) *Result {
   158  	return v.apply(Max{max, key}, obj)
   159  }
   160  
   161  // Range Test that the obj is between mni and max if obj's type is int
   162  func (v *Validation) Range(obj interface{}, min, max int, key string) *Result {
   163  	return v.apply(Range{Min{Min: min}, Max{Max: max}, key}, obj)
   164  }
   165  
   166  // MinSize Test that the obj is longer than min size if type is string or slice
   167  func (v *Validation) MinSize(obj interface{}, min int, key string) *Result {
   168  	return v.apply(MinSize{min, key}, obj)
   169  }
   170  
   171  // MaxSize Test that the obj is shorter than max size if type is string or slice
   172  func (v *Validation) MaxSize(obj interface{}, max int, key string) *Result {
   173  	return v.apply(MaxSize{max, key}, obj)
   174  }
   175  
   176  // Length Test that the obj is same length to n if type is string or slice
   177  func (v *Validation) Length(obj interface{}, n int, key string) *Result {
   178  	return v.apply(Length{n, key}, obj)
   179  }
   180  
   181  // Alpha Test that the obj is [a-zA-Z] if type is string
   182  func (v *Validation) Alpha(obj interface{}, key string) *Result {
   183  	return v.apply(Alpha{key}, obj)
   184  }
   185  
   186  // Numeric Test that the obj is [0-9] if type is string
   187  func (v *Validation) Numeric(obj interface{}, key string) *Result {
   188  	return v.apply(Numeric{key}, obj)
   189  }
   190  
   191  // AlphaNumeric Test that the obj is [0-9a-zA-Z] if type is string
   192  func (v *Validation) AlphaNumeric(obj interface{}, key string) *Result {
   193  	return v.apply(AlphaNumeric{key}, obj)
   194  }
   195  
   196  // Match Test that the obj matches regexp if type is string
   197  func (v *Validation) Match(obj interface{}, regex *regexp.Regexp, key string) *Result {
   198  	return v.apply(Match{regex, key}, obj)
   199  }
   200  
   201  // NoMatch Test that the obj doesn't match regexp if type is string
   202  func (v *Validation) NoMatch(obj interface{}, regex *regexp.Regexp, key string) *Result {
   203  	return v.apply(NoMatch{Match{Regexp: regex}, key}, obj)
   204  }
   205  
   206  // AlphaDash Test that the obj is [0-9a-zA-Z_-] if type is string
   207  func (v *Validation) AlphaDash(obj interface{}, key string) *Result {
   208  	return v.apply(AlphaDash{NoMatch{Match: Match{Regexp: alphaDashPattern}}, key}, obj)
   209  }
   210  
   211  // Email Test that the obj is email address if type is string
   212  func (v *Validation) Email(obj interface{}, key string) *Result {
   213  	return v.apply(Email{Match{Regexp: emailPattern}, key}, obj)
   214  }
   215  
   216  // IP Test that the obj is IP address if type is string
   217  func (v *Validation) IP(obj interface{}, key string) *Result {
   218  	return v.apply(IP{Match{Regexp: ipPattern}, key}, obj)
   219  }
   220  
   221  // Base64 Test that the obj is base64 encoded if type is string
   222  func (v *Validation) Base64(obj interface{}, key string) *Result {
   223  	return v.apply(Base64{Match{Regexp: base64Pattern}, key}, obj)
   224  }
   225  
   226  // Mobile Test that the obj is chinese mobile number if type is string
   227  func (v *Validation) Mobile(obj interface{}, key string) *Result {
   228  	return v.apply(Mobile{Match{Regexp: mobilePattern}, key}, obj)
   229  }
   230  
   231  // Tel Test that the obj is chinese telephone number if type is string
   232  func (v *Validation) Tel(obj interface{}, key string) *Result {
   233  	return v.apply(Tel{Match{Regexp: telPattern}, key}, obj)
   234  }
   235  
   236  // Phone Test that the obj is chinese mobile or telephone number if type is string
   237  func (v *Validation) Phone(obj interface{}, key string) *Result {
   238  	return v.apply(Phone{Mobile{Match: Match{Regexp: mobilePattern}},
   239  		Tel{Match: Match{Regexp: telPattern}}, key}, obj)
   240  }
   241  
   242  // ZipCode Test that the obj is chinese zip code if type is string
   243  func (v *Validation) ZipCode(obj interface{}, key string) *Result {
   244  	return v.apply(ZipCode{Match{Regexp: zipCodePattern}, key}, obj)
   245  }
   246  
   247  func (v *Validation) apply(chk Validator, obj interface{}) *Result {
   248  	if nil == obj {
   249  		if chk.IsSatisfied(obj) {
   250  			return &Result{Ok: true}
   251  		}
   252  	} else if reflect.TypeOf(obj).Kind() == reflect.Ptr {
   253  		if reflect.ValueOf(obj).IsNil() {
   254  			if chk.IsSatisfied(nil) {
   255  				return &Result{Ok: true}
   256  			}
   257  		} else {
   258  			if chk.IsSatisfied(reflect.ValueOf(obj).Elem().Interface()) {
   259  				return &Result{Ok: true}
   260  			}
   261  		}
   262  	} else if chk.IsSatisfied(obj) {
   263  		return &Result{Ok: true}
   264  	}
   265  
   266  	// Add the error to the validation context.
   267  	key := chk.GetKey()
   268  	Name := key
   269  	Field := ""
   270  	Label := ""
   271  	parts := strings.Split(key, ".")
   272  	if len(parts) == 3 {
   273  		Field = parts[0]
   274  		Name = parts[1]
   275  		Label = parts[2]
   276  		if len(Label) == 0 {
   277  			Label = Field
   278  		}
   279  	}
   280  
   281  	err := &Error{
   282  		Message:    Label + " " + chk.DefaultMessage(),
   283  		Key:        key,
   284  		Name:       Name,
   285  		Field:      Field,
   286  		Value:      obj,
   287  		Tmpl:       MessageTmpls[Name],
   288  		LimitValue: chk.GetLimitValue(),
   289  	}
   290  	v.setError(err)
   291  
   292  	// Also return it in the result.
   293  	return &Result{
   294  		Ok:    false,
   295  		Error: err,
   296  	}
   297  }
   298  
   299  // key must like aa.bb.cc or aa.bb.
   300  // AddError adds independent error message for the provided key
   301  func (v *Validation) AddError(key, message string) {
   302  	Name := key
   303  	Field := ""
   304  
   305  	Label := ""
   306  	parts := strings.Split(key, ".")
   307  	if len(parts) == 3 {
   308  		Field = parts[0]
   309  		Name = parts[1]
   310  		Label = parts[2]
   311  		if len(Label) == 0 {
   312  			Label = Field
   313  		}
   314  	}
   315  
   316  	err := &Error{
   317  		Message: Label + " " + message,
   318  		Key:     key,
   319  		Name:    Name,
   320  		Field:   Field,
   321  	}
   322  	v.setError(err)
   323  }
   324  
   325  func (v *Validation) setError(err *Error) {
   326  	v.Errors = append(v.Errors, err)
   327  	if v.ErrorsMap == nil {
   328  		v.ErrorsMap = make(map[string][]*Error)
   329  	}
   330  	if _, ok := v.ErrorsMap[err.Field]; !ok {
   331  		v.ErrorsMap[err.Field] = []*Error{}
   332  	}
   333  	v.ErrorsMap[err.Field] = append(v.ErrorsMap[err.Field], err)
   334  }
   335  
   336  // SetError Set error message for one field in ValidationError
   337  func (v *Validation) SetError(fieldName string, errMsg string) *Error {
   338  	err := &Error{Key: fieldName, Field: fieldName, Tmpl: errMsg, Message: errMsg}
   339  	v.setError(err)
   340  	return err
   341  }
   342  
   343  // Check Apply a group of validators to a field, in order, and return the
   344  // ValidationResult from the first one that fails, or the last one that
   345  // succeeds.
   346  func (v *Validation) Check(obj interface{}, checks ...Validator) *Result {
   347  	var result *Result
   348  	for _, check := range checks {
   349  		result = v.apply(check, obj)
   350  		if !result.Ok {
   351  			return result
   352  		}
   353  	}
   354  	return result
   355  }
   356  
   357  // Valid Validate a struct.
   358  // the obj parameter must be a struct or a struct pointer
   359  func (v *Validation) Valid(obj interface{}) (b bool, err error) {
   360  	objT := reflect.TypeOf(obj)
   361  	objV := reflect.ValueOf(obj)
   362  	switch {
   363  	case isStruct(objT):
   364  	case isStructPtr(objT):
   365  		objT = objT.Elem()
   366  		objV = objV.Elem()
   367  	default:
   368  		err = fmt.Errorf("%v must be a struct or a struct pointer", obj)
   369  		return
   370  	}
   371  
   372  	for i := 0; i < objT.NumField(); i++ {
   373  		var vfs []ValidFunc
   374  		if vfs, err = getValidFuncs(objT.Field(i)); err != nil {
   375  			return
   376  		}
   377  
   378  		var hasRequired bool
   379  		for _, vf := range vfs {
   380  			if vf.Name == "Required" {
   381  				hasRequired = true
   382  			}
   383  
   384  			currentField := objV.Field(i).Interface()
   385  			if objV.Field(i).Kind() == reflect.Ptr {
   386  				if objV.Field(i).IsNil() {
   387  					currentField = ""
   388  				} else {
   389  					currentField = objV.Field(i).Elem().Interface()
   390  				}
   391  			}
   392  
   393  			chk := Required{""}.IsSatisfied(currentField)
   394  			if !hasRequired && v.RequiredFirst && !chk {
   395  				if _, ok := CanSkipFuncs[vf.Name]; ok {
   396  					continue
   397  				}
   398  			}
   399  
   400  			if _, err = funcs.Call(vf.Name,
   401  				mergeParam(v, objV.Field(i).Interface(), vf.Params)...); err != nil {
   402  				return
   403  			}
   404  		}
   405  	}
   406  
   407  	if !v.HasErrors() {
   408  		if form, ok := obj.(ValidFormer); ok {
   409  			form.Valid(v)
   410  		}
   411  	}
   412  
   413  	return !v.HasErrors(), nil
   414  }
   415  
   416  // RecursiveValid Recursively validate a struct.
   417  // Step1: Validate by v.Valid
   418  // Step2: If pass on step1, then reflect obj's fields
   419  // Step3: Do the Recursively validation to all struct or struct pointer fields
   420  func (v *Validation) RecursiveValid(objc interface{}) (bool, error) {
   421  	//Step 1: validate obj itself firstly
   422  	// fails if objc is not struct
   423  	pass, err := v.Valid(objc)
   424  	if err != nil || !pass {
   425  		return pass, err // Stop recursive validation
   426  	}
   427  	// Step 2: Validate struct's struct fields
   428  	objT := reflect.TypeOf(objc)
   429  	objV := reflect.ValueOf(objc)
   430  
   431  	if isStructPtr(objT) {
   432  		objT = objT.Elem()
   433  		objV = objV.Elem()
   434  	}
   435  
   436  	for i := 0; i < objT.NumField(); i++ {
   437  
   438  		t := objT.Field(i).Type
   439  
   440  		// Recursive applies to struct or pointer to structs fields
   441  		if isStruct(t) || isStructPtr(t) {
   442  			// Step 3: do the recursive validation
   443  			// Only valid the Public field recursively
   444  			if objV.Field(i).CanInterface() {
   445  				pass, err = v.RecursiveValid(objV.Field(i).Interface())
   446  			}
   447  		}
   448  	}
   449  	return pass, err
   450  }
   451  
   452  func (v *Validation) CanSkipAlso(skipFunc string) {
   453  	if _, ok := CanSkipFuncs[skipFunc]; !ok {
   454  		CanSkipFuncs[skipFunc] = struct{}{}
   455  	}
   456  }