github.com/zhongdalu/gf@v1.0.0/g/util/gvalid/gvalid_check_struct.go (about)

     1  // Copyright 2017-2018 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/zhongdalu/gf.
     6  
     7  package gvalid
     8  
     9  import (
    10  	"strings"
    11  
    12  	"github.com/zhongdalu/gf/g/internal/structs"
    13  	"github.com/zhongdalu/gf/g/util/gconv"
    14  )
    15  
    16  var (
    17  	// 同时支持gvalid、valid和v标签,优先使用gvalid
    18  	structTagPriority = []string{"gvalid", "valid", "v"}
    19  )
    20  
    21  // 校验struct对象属性,object参数也可以是一个指向对象的指针,返回值同CheckMap方法。
    22  // struct的数据校验结果信息是顺序的。
    23  func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Error {
    24  	// 字段别名记录,用于msgs覆盖struct tag的
    25  	fieldAliases := make(map[string]string)
    26  	params := make(map[string]interface{})
    27  	checkRules := make(map[string]string)
    28  	customMsgs := make(CustomMsg)
    29  	// 返回的顺序规则
    30  	errorRules := make([]string, 0)
    31  	// 返回的校验错误
    32  	errorMaps := make(ErrorMap)
    33  	// 解析rules参数
    34  	switch v := rules.(type) {
    35  	// 支持校验错误顺序: []sequence tag
    36  	case []string:
    37  		for _, tag := range v {
    38  			name, rule, msg := parseSequenceTag(tag)
    39  			if len(name) == 0 {
    40  				continue
    41  			}
    42  			// 错误提示
    43  			if len(msg) > 0 {
    44  				ruleArray := strings.Split(rule, "|")
    45  				msgArray := strings.Split(msg, "|")
    46  				for k, v := range ruleArray {
    47  					// 如果msg条数比rule少,那么多余的rule使用默认的错误信息
    48  					if len(msgArray) <= k {
    49  						continue
    50  					}
    51  					if len(msgArray[k]) == 0 {
    52  						continue
    53  					}
    54  					array := strings.Split(v, ":")
    55  					if _, ok := customMsgs[name]; !ok {
    56  						customMsgs[name] = make(map[string]string)
    57  					}
    58  					customMsgs[name].(map[string]string)[strings.TrimSpace(array[0])] = strings.TrimSpace(msgArray[k])
    59  				}
    60  			}
    61  			checkRules[name] = rule
    62  			errorRules = append(errorRules, name+"@"+rule)
    63  		}
    64  
    65  	// 不支持校验错误顺序: map[键名]校验规则
    66  	case map[string]string:
    67  		checkRules = v
    68  	}
    69  	// 首先, 按照属性循环一遍将struct的属性、数值、tag解析
    70  	for nameOrTag, field := range structs.MapField(object, structTagPriority, true) {
    71  		fieldName := field.Name()
    72  		params[fieldName] = field.Value()
    73  		// MapField返回map[name/tag]*Field,当nameOrTag != fieldName时,nameOrTag为tag
    74  		if nameOrTag != fieldName {
    75  			// sequence tag == struct tag, 这里的name为别名
    76  			name, rule, msg := parseSequenceTag(nameOrTag)
    77  			if len(name) == 0 {
    78  				name = fieldName
    79  			} else {
    80  				fieldAliases[fieldName] = name
    81  			}
    82  			// params参数使用别名**扩容**(而不仅仅使用别名),仅用于验证使用
    83  			if _, ok := params[name]; !ok {
    84  				params[name] = field.Value()
    85  			}
    86  			// 校验规则
    87  			if _, ok := checkRules[name]; !ok {
    88  				if _, ok := checkRules[fieldName]; ok {
    89  					// tag中存在别名,且rules传入的参数中使用了属性命名时,进行规则替换,并删除该属性的规则
    90  					checkRules[name] = checkRules[fieldName]
    91  					delete(checkRules, fieldName)
    92  				} else {
    93  					checkRules[name] = rule
    94  				}
    95  				errorRules = append(errorRules, name+"@"+rule)
    96  			} else {
    97  				// 传递的rules规则会覆盖struct tag的规则
    98  				continue
    99  			}
   100  			// 错误提示
   101  			if len(msg) > 0 {
   102  				ruleArray := strings.Split(rule, "|")
   103  				msgArray := strings.Split(msg, "|")
   104  				for k, v := range ruleArray {
   105  					// 如果msg条数比rule少,那么多余的rule使用默认的错误信息
   106  					if len(msgArray) <= k {
   107  						continue
   108  					}
   109  					if len(msgArray[k]) == 0 {
   110  						continue
   111  					}
   112  					array := strings.Split(v, ":")
   113  					if _, ok := customMsgs[name]; !ok {
   114  						customMsgs[name] = make(map[string]string)
   115  					}
   116  					customMsgs[name].(map[string]string)[strings.TrimSpace(array[0])] = strings.TrimSpace(msgArray[k])
   117  				}
   118  			}
   119  		}
   120  	}
   121  
   122  	// 自定义错误消息,非必须参数,优先级比rules参数中以及struct tag中定义的错误消息更高
   123  	if len(msgs) > 0 && len(msgs[0]) > 0 {
   124  		for k, v := range msgs[0] {
   125  			if a, ok := fieldAliases[k]; ok {
   126  				// 属性的别名存在时,覆盖别名的错误信息
   127  				customMsgs[a] = v
   128  			} else {
   129  				customMsgs[k] = v
   130  			}
   131  		}
   132  	}
   133  
   134  	/* 以下逻辑和CheckMap相同 */
   135  
   136  	// 开始执行校验: 以校验规则作为基础进行遍历校验
   137  	var value interface{}
   138  	// 这里的rule变量为多条校验规则,不包含名字或者错误信息定义
   139  	for key, rule := range checkRules {
   140  		value = nil
   141  		if v, ok := params[key]; ok {
   142  			value = v
   143  		}
   144  		if e := Check(value, rule, customMsgs[key], params); e != nil {
   145  			_, item := e.FirstItem()
   146  			// 如果值为nil|"",并且不需要require*验证时,其他验证失效
   147  			if value == nil || gconv.String(value) == "" {
   148  				required := false
   149  				// rule => error
   150  				for k := range item {
   151  					if _, ok := mustCheckRulesEvenValueEmpty[k]; ok {
   152  						required = true
   153  						break
   154  					}
   155  				}
   156  				if !required {
   157  					continue
   158  				}
   159  			}
   160  			if _, ok := errorMaps[key]; !ok {
   161  				errorMaps[key] = make(map[string]string)
   162  			}
   163  			for k, v := range item {
   164  				errorMaps[key][k] = v
   165  			}
   166  		}
   167  	}
   168  	if len(errorMaps) > 0 {
   169  		return newError(errorRules, errorMaps)
   170  	}
   171  	return nil
   172  }