gf/util/gvalid/gvalid_check_struct.go
2020-04-20 22:36:28 +08:00

175 lines
5.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gvalid
import (
"strings"
"github.com/gogf/gf/internal/structs"
"github.com/gogf/gf/util/gconv"
)
var (
structTagPriority = []string{"gvalid", "valid", "v"} // structTagPriority specifies the validation tag priority array.
aliasNameTagPriority = []string{"param", "params", "p"} // aliasNameTagPriority specifies the alias tag priority array.
)
// 校验struct对象属性object参数也可以是一个指向对象的指针返回值同CheckMap方法。
// struct的数据校验结果信息是顺序的。
func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error {
var (
params = make(map[string]interface{})
checkRules = make(map[string]string)
customMessage = make(CustomMsg)
fieldAliases = make(map[string]string) // Alias names for <messages> overwriting struct tag names.
errorRules = make([]string, 0) // Sequence rules.
errorMaps = make(ErrorMap) // Returned error
)
// 解析rules参数
switch v := rules.(type) {
// 支持校验错误顺序: []sequence tag
case []string:
for _, tag := range v {
name, rule, msg := parseSequenceTag(tag)
if len(name) == 0 {
continue
}
// 错误提示
if len(msg) > 0 {
ruleArray := strings.Split(rule, "|")
msgArray := strings.Split(msg, "|")
for k, v := range ruleArray {
// 如果msg条数比rule少那么多余的rule使用默认的错误信息
if len(msgArray) <= k {
continue
}
if len(msgArray[k]) == 0 {
continue
}
array := strings.Split(v, ":")
if _, ok := customMessage[name]; !ok {
customMessage[name] = make(map[string]string)
}
customMessage[name].(map[string]string)[strings.TrimSpace(array[0])] = strings.TrimSpace(msgArray[k])
}
}
checkRules[name] = rule
errorRules = append(errorRules, name+"@"+rule)
}
// Map type rules does not support sequence.
// Format: map[key]rule
case map[string]string:
checkRules = v
}
// Checks and extends the parameters map with struct alias tag.
for nameOrTag, field := range structs.MapField(object, aliasNameTagPriority, true) {
params[nameOrTag] = field.Value()
params[field.Name()] = field.Value()
}
// 首先, 按照属性循环一遍将struct的属性、数值、tag解析
// It here must use structs.TagFields not structs.MapField to ensure error sequence.
for _, field := range structs.TagFields(object, structTagPriority, true) {
fieldName := field.Name()
// sequence tag == struct tag, 这里的name为别名
name, rule, msg := parseSequenceTag(field.Tag)
if len(name) == 0 {
name = fieldName
} else {
fieldAliases[fieldName] = name
}
// params参数使用别名**扩容**(而不仅仅使用别名),仅用于验证使用
if _, ok := params[name]; !ok {
params[name] = field.Value()
}
// 校验规则
if _, ok := checkRules[name]; !ok {
if _, ok := checkRules[fieldName]; ok {
// tag中存在别名且rules传入的参数中使用了属性命名时进行规则替换并删除该属性的规则
checkRules[name] = checkRules[fieldName]
delete(checkRules, fieldName)
} else {
checkRules[name] = rule
}
errorRules = append(errorRules, name+"@"+rule)
} else {
// 传递的rules规则会覆盖struct tag的规则
continue
}
// 错误提示
if len(msg) > 0 {
ruleArray := strings.Split(rule, "|")
msgArray := strings.Split(msg, "|")
for k, v := range ruleArray {
// 如果msg条数比rule少那么多余的rule使用默认的错误信息
if len(msgArray) <= k {
continue
}
if len(msgArray[k]) == 0 {
continue
}
array := strings.Split(v, ":")
if _, ok := customMessage[name]; !ok {
customMessage[name] = make(map[string]string)
}
customMessage[name].(map[string]string)[strings.TrimSpace(array[0])] = strings.TrimSpace(msgArray[k])
}
}
}
// 自定义错误消息非必须参数优先级比rules参数中以及struct tag中定义的错误消息更高
if len(messages) > 0 && len(messages[0]) > 0 {
for k, v := range messages[0] {
if a, ok := fieldAliases[k]; ok {
// 属性的别名存在时,覆盖别名的错误信息
customMessage[a] = v
} else {
customMessage[k] = v
}
}
}
/* 以下逻辑和CheckMap相同 */
// 开始执行校验: 以校验规则作为基础进行遍历校验
var value interface{}
// 这里的rule变量为多条校验规则不包含名字或者错误信息定义
for key, rule := range checkRules {
value = nil
if v, ok := params[key]; ok {
value = v
}
if e := Check(value, rule, customMessage[key], params); e != nil {
_, item := e.FirstItem()
// 如果值为nil|""并且不需要require*验证时,其他验证失效
if value == nil || gconv.String(value) == "" {
required := false
// rule => error
for k := range item {
if _, ok := mustCheckRulesEvenValueEmpty[k]; ok {
required = true
break
}
}
if !required {
continue
}
}
if _, ok := errorMaps[key]; !ok {
errorMaps[key] = make(map[string]string)
}
for k, v := range item {
errorMaps[key][k] = v
}
}
}
if len(errorMaps) > 0 {
return newError(errorRules, errorMaps)
}
return nil
}