123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- package validation
- import (
- "fmt"
- "reflect"
- "regexp"
- "strings"
- "github.com/coscms/tagfast"
- )
- type ValidFormer interface {
- Valid(*Validation)
- }
- type ValidationError struct {
- Message, Key, Name, Field, Tmpl string
- Value interface{}
- LimitValue interface{}
- }
- // Returns the Message.
- func (e *ValidationError) String() string {
- if e == nil {
- return ""
- }
- return e.Message
- }
- // A ValidationResult is returned from every validation method.
- // It provides an indication of success, and a pointer to the Error (if any).
- type ValidationResult struct {
- Error *ValidationError
- Ok bool
- }
- func (r *ValidationResult) Key(key string) *ValidationResult {
- if r.Error != nil {
- r.Error.Key = key
- }
- return r
- }
- func (r *ValidationResult) Message(message string, args ...interface{}) *ValidationResult {
- if r.Error != nil {
- if len(args) == 0 {
- r.Error.Message = message
- } else {
- r.Error.Message = fmt.Sprintf(message, args...)
- }
- }
- return r
- }
- // A Validation context manages data validation and error messages.
- type Validation struct {
- Errors []*ValidationError
- ErrorsMap map[string]*ValidationError
- }
- func (v *Validation) Clear() {
- v.Errors = []*ValidationError{}
- }
- func (v *Validation) HasErrors() bool {
- return len(v.Errors) > 0
- }
- // Return the errors mapped by key.
- // If there are multiple validation errors associated with a single key, the
- // first one "wins". (Typically the first validation will be the more basic).
- func (v *Validation) ErrorMap() map[string]*ValidationError {
- return v.ErrorsMap
- }
- // Add an error to the validation context.
- func (v *Validation) Error(message string, args ...interface{}) *ValidationResult {
- result := (&ValidationResult{
- Ok: false,
- Error: &ValidationError{},
- }).Message(message, args...)
- v.Errors = append(v.Errors, result.Error)
- return result
- }
- // Test that the argument is non-nil and non-empty (if string or list)
- func (v *Validation) Required(obj interface{}, key string) *ValidationResult {
- return v.apply(Required{key}, obj)
- }
- // Test that the obj is greater than min if obj's type is int
- func (v *Validation) Min(obj interface{}, min int, key string) *ValidationResult {
- return v.apply(Min{min, key}, obj)
- }
- // Test that the obj is less than max if obj's type is int
- func (v *Validation) Max(obj interface{}, max int, key string) *ValidationResult {
- return v.apply(Max{max, key}, obj)
- }
- // Test that the obj is between mni and max if obj's type is int
- func (v *Validation) Range(obj interface{}, min, max int, key string) *ValidationResult {
- return v.apply(Range{Min{Min: min}, Max{Max: max}, key}, obj)
- }
- func (v *Validation) MinSize(obj interface{}, min int, key string) *ValidationResult {
- return v.apply(MinSize{min, key}, obj)
- }
- func (v *Validation) MaxSize(obj interface{}, max int, key string) *ValidationResult {
- return v.apply(MaxSize{max, key}, obj)
- }
- func (v *Validation) Length(obj interface{}, n int, key string) *ValidationResult {
- return v.apply(Length{n, key}, obj)
- }
- func (v *Validation) Alpha(obj interface{}, key string) *ValidationResult {
- return v.apply(Alpha{key}, obj)
- }
- func (v *Validation) Numeric(obj interface{}, key string) *ValidationResult {
- return v.apply(Numeric{key}, obj)
- }
- func (v *Validation) AlphaNumeric(obj interface{}, key string) *ValidationResult {
- return v.apply(AlphaNumeric{key}, obj)
- }
- func (v *Validation) Match(obj interface{}, regex *regexp.Regexp, key string) *ValidationResult {
- return v.apply(Match{regex, key}, obj)
- }
- func (v *Validation) NoMatch(obj interface{}, regex *regexp.Regexp, key string) *ValidationResult {
- return v.apply(NoMatch{Match{Regexp: regex}, key}, obj)
- }
- func (v *Validation) AlphaDash(obj interface{}, key string) *ValidationResult {
- return v.apply(AlphaDash{NoMatch{Match: Match{Regexp: alphaDashPattern}}, key}, obj)
- }
- func (v *Validation) Email(obj interface{}, key string) *ValidationResult {
- return v.apply(Email{Match{Regexp: emailPattern}, key}, obj)
- }
- func (v *Validation) IP(obj interface{}, key string) *ValidationResult {
- return v.apply(IP{Match{Regexp: ipPattern}, key}, obj)
- }
- func (v *Validation) Base64(obj interface{}, key string) *ValidationResult {
- return v.apply(Base64{Match{Regexp: base64Pattern}, key}, obj)
- }
- func (v *Validation) Mobile(obj interface{}, key string) *ValidationResult {
- return v.apply(Mobile{Match{Regexp: mobilePattern}, key}, obj)
- }
- func (v *Validation) Tel(obj interface{}, key string) *ValidationResult {
- return v.apply(Tel{Match{Regexp: telPattern}, key}, obj)
- }
- func (v *Validation) Phone(obj interface{}, key string) *ValidationResult {
- return v.apply(Phone{Mobile{Match: Match{Regexp: mobilePattern}},
- Tel{Match: Match{Regexp: telPattern}}, key}, obj)
- }
- func (v *Validation) ZipCode(obj interface{}, key string) *ValidationResult {
- return v.apply(ZipCode{Match{Regexp: zipCodePattern}, key}, obj)
- }
- func (v *Validation) apply(chk Validator, obj interface{}) *ValidationResult {
- if chk.IsSatisfied(obj) {
- return &ValidationResult{Ok: true}
- }
- // Add the error to the validation context.
- key := chk.GetKey()
- Field := key
- Name := ""
- parts := strings.Split(key, "|")
- if len(parts) == 2 {
- Field = parts[0]
- Name = parts[1]
- }
- err := &ValidationError{
- Message: chk.DefaultMessage(),
- Key: key,
- Name: Name,
- Field: Field,
- Value: obj,
- Tmpl: MessageTmpls[Name],
- LimitValue: chk.GetLimitValue(),
- }
- v.setError(err)
- // Also return it in the result.
- return &ValidationResult{
- Ok: false,
- Error: err,
- }
- }
- func (v *Validation) setError(err *ValidationError) {
- v.Errors = append(v.Errors, err)
- if v.ErrorsMap == nil {
- v.ErrorsMap = make(map[string]*ValidationError)
- }
- if _, ok := v.ErrorsMap[err.Field]; !ok {
- v.ErrorsMap[err.Field] = err
- }
- }
- func (v *Validation) SetError(fieldName string, errMsg string) *ValidationError {
- err := &ValidationError{Key: fieldName, Field: fieldName, Tmpl: errMsg, Message: errMsg}
- v.setError(err)
- return err
- }
- // Apply a group of validators to a field, in order, and return the
- // ValidationResult from the first one that fails, or the last one that
- // succeeds.
- func (v *Validation) Check(obj interface{}, checks ...Validator) *ValidationResult {
- var result *ValidationResult
- for _, check := range checks {
- result = v.apply(check, obj)
- if !result.Ok {
- return result
- }
- }
- return result
- }
- // the obj parameter must be a struct or a struct pointer
- func (v *Validation) Valid(obj interface{}, args ...string) (b bool, err error) {
- err = v.validExec(obj, "", args...)
- if err != nil {
- fmt.Println(err)
- return
- }
- if !v.HasErrors() {
- if form, ok := obj.(ValidFormer); ok {
- form.Valid(v)
- }
- }
- return !v.HasErrors(), nil
- }
- func (v *Validation) validExec(obj interface{}, baseName string, args ...string) (err error) {
- objT := reflect.TypeOf(obj)
- objV := reflect.ValueOf(obj)
- switch {
- case isStruct(objT):
- case isStructPtr(objT):
- objT = objT.Elem()
- objV = objV.Elem()
- default:
- err = fmt.Errorf("%v must be a struct or a struct pointer", obj)
- return
- }
- var chkFields map[string][]string = make(map[string][]string)
- var pNum int = len(args)
- //fmt.Println(objT.Name(), ":[Struct NumIn]", pNum)
- if pNum > 0 {
- //aa.b.c,ab.b.c
- for _, v := range args {
- arr := strings.SplitN(v, ".", 2)
- if _, ok := chkFields[arr[0]]; !ok {
- chkFields[arr[0]] = make([]string, 0)
- }
- if len(arr) > 1 {
- chkFields[arr[0]] = append(chkFields[arr[0]], arr[1])
- }
- }
- }
- args = make([]string, 0)
- if len(chkFields) > 0 { //检测指定字段
- for field, args := range chkFields {
- f, ok := objT.FieldByName(field)
- if !ok {
- err = fmt.Errorf("No name for the '%s' field", field)
- return
- }
- tag := tagfast.Tag(objT, f, VALIDTAG)
- if tag == "-" {
- continue
- }
- var vfs []ValidFunc
- var fName string
- if baseName == "" {
- fName = f.Name
- } else {
- fName = strings.Join([]string{baseName, f.Name}, ".")
- }
- fv := objV.FieldByName(field)
- if isStruct(f.Type) || isStructPtr(f.Type) {
- if fv.CanInterface() {
- err = v.validExec(fv.Interface(), fName, args...)
- }
- continue
- }
- if vfs, err = getValidFuncs(f, objT, fName); err != nil {
- return
- }
- for _, vf := range vfs {
- if _, err = funcs.Call(vf.Name,
- mergeParam(v, fv.Interface(), vf.Params)...); err != nil {
- return
- }
- }
- }
- } else { //检测全部字段
- for i := 0; i < objT.NumField(); i++ {
- tag := tagfast.Tag(objT, objT.Field(i), VALIDTAG)
- if tag == "-" {
- continue
- }
- var vfs []ValidFunc
- var fName string
- if baseName == "" {
- fName = objT.Field(i).Name
- } else {
- fName = strings.Join([]string{baseName, objT.Field(i).Name}, ".")
- }
- //fmt.Println(fName, ":[Type]:", objT.Field(i).Type.Kind())
- if isStruct(objT.Field(i).Type) || isStructPtr(objT.Field(i).Type) {
- if objV.Field(i).CanInterface() {
- err = v.validExec(objV.Field(i).Interface(), fName)
- }
- continue
- }
- if vfs, err = getValidFuncs(objT.Field(i), objT, fName); err != nil {
- return
- }
- for _, vf := range vfs {
- if _, err = funcs.Call(vf.Name,
- mergeParam(v, objV.Field(i).Interface(), vf.Params)...); err != nil {
- return
- }
- }
- }
- }
- return
- }
|