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 }