github.com/profzone/eden-framework@v1.0.10/pkg/courier/transport_http/transform/scanner.go (about)

     1  package transform
     2  
     3  import (
     4  	"encoding"
     5  	"encoding/json"
     6  	"fmt"
     7  	"go/ast"
     8  	"reflect"
     9  
    10  	"github.com/profzone/eden-framework/pkg/reflectx"
    11  	"github.com/profzone/eden-framework/pkg/strings"
    12  	"github.com/profzone/eden-framework/pkg/validate"
    13  )
    14  
    15  type Validator func() (isValid bool, msg string)
    16  
    17  func NewScanner() *Scanner {
    18  	return &Scanner{}
    19  }
    20  
    21  type Scanner struct {
    22  	walker    PathWalker
    23  	errMsgMap ErrMsgMap
    24  }
    25  
    26  func (vs *Scanner) Validate(rv reflect.Value, tpe reflect.Type) (bool, ErrMsgMap) {
    27  	vs.marshalAndValidate(rv, tpe, "", false, "", "")
    28  	return len(vs.errMsgMap) == 0, vs.errMsgMap
    29  }
    30  
    31  func (vs *Scanner) setErrMsg(path string, msg string) {
    32  	if vs.errMsgMap == nil {
    33  		vs.errMsgMap = ErrMsgMap{}
    34  	}
    35  	vs.errMsgMap[path] = msg
    36  }
    37  
    38  func (vs *Scanner) marshalAndValidate(rv reflect.Value, tpe reflect.Type, defaultValue string, required bool, tagValidate string, tagErrMsg string) {
    39  	v := rv.Interface()
    40  	if rv.Kind() != reflect.Ptr {
    41  		v = rv.Addr().Interface()
    42  	}
    43  
    44  	if _, ok := v.(encoding.TextUnmarshaler); ok {
    45  		errMsg := MarshalAndValidate(rv, tpe, defaultValue, required, tagValidate, tagErrMsg)
    46  		if errMsg != "" {
    47  			vs.setErrMsg(vs.walker.String(), errMsg)
    48  		}
    49  		return
    50  	}
    51  
    52  	if _, ok := v.(json.Unmarshaler); ok {
    53  		errMsg := MarshalAndValidate(rv, tpe, defaultValue, required, tagValidate, tagErrMsg)
    54  		if errMsg != "" {
    55  			vs.setErrMsg(vs.walker.String(), errMsg)
    56  		}
    57  		return
    58  	}
    59  
    60  	tpe = reflectx.IndirectType(tpe)
    61  
    62  	switch tpe.Kind() {
    63  	case reflect.Struct:
    64  		if rv.Kind() == reflect.Ptr {
    65  			errMsg := MarshalAndValidate(rv, tpe, defaultValue, required, tagValidate, tagErrMsg)
    66  			if errMsg != "" {
    67  				vs.setErrMsg(vs.walker.String(), errMsg)
    68  				return
    69  			}
    70  
    71  			if rv.IsNil() {
    72  				return
    73  			}
    74  		}
    75  
    76  		rv = reflectx.Indirect(rv)
    77  
    78  		for i := 0; i < tpe.NumField(); i++ {
    79  			field := tpe.Field(i)
    80  			if !ast.IsExported(field.Name) {
    81  				continue
    82  			}
    83  
    84  			jsonTag, exists, jsonFlags := GetTagJSON(&field)
    85  			if (exists && jsonTag != "-") || field.Anonymous {
    86  				if !field.Anonymous {
    87  					vs.walker.Enter(GetStructFieldDisplayName(&field))
    88  				}
    89  
    90  				tagValidate, _ := GetTagValidate(&field)
    91  				defaultValue, hasDefaultValue := GetTagDefault(&field)
    92  				tagErrMsg, _ := GetTagErrMsg(&field)
    93  
    94  				required := true
    95  				if hasOmitempty, ok := jsonFlags["omitempty"]; ok {
    96  					required = !hasOmitempty
    97  				} else {
    98  					// todo don't use non-default as required
    99  					required = !hasDefaultValue
   100  				}
   101  
   102  				vs.marshalAndValidate(rv.Field(i), field.Type, defaultValue, required, tagValidate, tagErrMsg)
   103  
   104  				if rv.NumMethod() > 0 {
   105  					validateHook := rv.MethodByName(fmt.Sprintf("Validate%s", field.Name))
   106  					if validateHook.IsValid() {
   107  						if validateFn, ok := validateHook.Interface().(func() string); ok {
   108  							msg := validateFn()
   109  							if msg != "" {
   110  								vs.setErrMsg(vs.walker.String(), msg)
   111  							}
   112  						}
   113  					}
   114  				}
   115  
   116  				if !field.Anonymous {
   117  					vs.walker.Exit()
   118  				}
   119  			}
   120  		}
   121  	case reflect.Slice, reflect.Array:
   122  		if tagValidate != "" {
   123  			isValid, msg := validate.ValidateItem(tagValidate, rv.Interface(), tagErrMsg)
   124  			if !isValid {
   125  				vs.setErrMsg(vs.walker.String(), msg)
   126  			}
   127  		}
   128  		for i := 0; i < rv.Len(); i++ {
   129  			vs.walker.Enter(i)
   130  			vs.marshalAndValidate(rv.Index(i), tpe.Elem(), "", false, "", tagErrMsg)
   131  			vs.walker.Exit()
   132  		}
   133  	default:
   134  		errMsg := MarshalAndValidate(rv, tpe, defaultValue, required, tagValidate, tagErrMsg)
   135  		if errMsg != "" {
   136  			vs.setErrMsg(vs.walker.String(), errMsg)
   137  		}
   138  	}
   139  }
   140  
   141  var ErrMsgForRequired = "缺失必填字段"
   142  
   143  func MarshalAndValidate(
   144  	rv reflect.Value, tpe reflect.Type,
   145  	defaultValue string, isRequired bool,
   146  	tagValidate string, tagErrMsg string,
   147  ) string {
   148  	isPtr := rv.Kind() == reflect.Ptr
   149  
   150  	if isPtr {
   151  		if rv.IsNil() {
   152  			if isRequired {
   153  				return ErrMsgForRequired
   154  			}
   155  
   156  			// when not required,should set value
   157  			if tpe.Kind() != reflect.Struct && defaultValue != "" && rv.CanSet() {
   158  				rv.Set(reflect.New(reflectx.IndirectType(tpe)))
   159  				rv = reflect.Indirect(rv)
   160  
   161  				err := str.ConvertFromStr(defaultValue, rv)
   162  				if err != nil {
   163  					return fmt.Sprintf("%s can't set wrong default value %s", rv.Type().Name(), defaultValue)
   164  				}
   165  			}
   166  		}
   167  
   168  		if tagValidate != "" {
   169  			rv = reflect.Indirect(rv)
   170  			isValid, msg := validate.ValidateItem(tagValidate, rv.Interface(), tagErrMsg)
   171  			if !isValid {
   172  				return msg
   173  			}
   174  		}
   175  
   176  		return ""
   177  	}
   178  
   179  	rv = reflect.Indirect(rv)
   180  	isEmptyValue := reflectx.IsEmptyValue(rv)
   181  
   182  	if isEmptyValue && isRequired {
   183  		return ErrMsgForRequired
   184  	}
   185  
   186  	// only empty value can set default
   187  	if isEmptyValue && defaultValue != "" && rv.CanSet() {
   188  		err := str.ConvertFromStr(defaultValue, rv)
   189  		if err != nil {
   190  			return fmt.Sprintf("%s can't set wrong default value %s", rv.Type().Name(), defaultValue)
   191  		}
   192  	}
   193  
   194  	if tagValidate != "" {
   195  		isValid, msg := validate.ValidateItem(tagValidate, rv.Interface(), tagErrMsg)
   196  		if !isValid {
   197  			return msg
   198  		}
   199  	}
   200  
   201  	return ""
   202  }
   203  
   204  type PathWalker struct {
   205  	path []interface{}
   206  }
   207  
   208  func (pw *PathWalker) Enter(i interface{}) {
   209  	pw.path = append(pw.path, i)
   210  }
   211  
   212  func (pw *PathWalker) Exit() {
   213  	pw.path = pw.path[:len(pw.path)-1]
   214  }
   215  
   216  func (pw *PathWalker) Paths() []interface{} {
   217  	return pw.path
   218  }
   219  
   220  func (pw *PathWalker) String() string {
   221  	pathString := ""
   222  	for i := 0; i < len(pw.path); i++ {
   223  		switch pw.path[i].(type) {
   224  		case string:
   225  			if pathString != "" {
   226  				pathString += "."
   227  			}
   228  			pathString += pw.path[i].(string)
   229  		case int:
   230  			pathString += fmt.Sprintf("[%d]", pw.path[i].(int))
   231  		}
   232  	}
   233  	return pathString
   234  }