gf/g/util/gvalid/gvalid_check_struct.go

154 lines
5.8 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://gitee.com/johng/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://gitee.com/johng/gf.
package gvalid
import (
"gitee.com/johng/gf/g/util/gconv"
"gitee.com/johng/gf/third/github.com/fatih/structs"
"strings"
)
// 校验struct对象属性object参数也可以是一个指向对象的指针返回值同CheckMap方法。
// struct的数据校验结果信息是顺序的。
func CheckStruct(object interface{}, rules interface{}, msgs...CustomMsg) *Error {
fields := structs.Fields(object)
params := make(map[string]interface{})
checkRules := make(map[string]string)
customMsgs := make(CustomMsg)
// 返回的顺序规则
errorRules := make([]string, 0)
// 返回的校验错误
errorMaps := make(ErrorMap)
// 解析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 := customMsgs[name]; !ok {
customMsgs[name] = make(map[string]string)
}
customMsgs[name].(map[string]string)[strings.TrimSpace(array[0])] = strings.TrimSpace(msgArray[k])
}
}
checkRules[name] = rule
errorRules = append(errorRules, name + "@" + rule)
}
// 不支持校验错误顺序: map[键名]校验规则
case map[string]string:
checkRules = v
}
// 首先, 按照属性循环一遍将strcut的属性、数值、tag解析
for _, field := range fields {
params[field.Name()] = field.Value()
if tag := field.Tag("gvalid"); tag != "" {
// sequence tag == struct tag, 这里的name为别名
name, rule, msg := parseSequenceTag(tag)
if len(name) == 0 {
name = field.Name()
}
// params参数使用别名**扩容**(而不仅仅使用别名),仅用于验证使用
if _, ok := params[name]; !ok {
params[name] = field.Value()
}
// 校验规则
if _, ok := checkRules[name]; !ok {
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 := customMsgs[name]; !ok {
customMsgs[name] = make(map[string]string)
}
customMsgs[name].(map[string]string)[strings.TrimSpace(array[0])] = strings.TrimSpace(msgArray[k])
}
}
}
}
// 自定义错误消息非必须参数优先级比rules参数中以及struct tag中定义的错误消息更高
if len(msgs) > 0 && len(msgs[0]) > 0 {
if len(customMsgs) > 0 {
for k, v := range msgs[0] {
customMsgs[k] = v
}
} else {
customMsgs = msgs[0]
}
}
/* 以下逻辑和CheckMap相同 */
// 开始执行校验: 以校验规则作为基础进行遍历校验
value := (interface{})(nil)
// 这里的rule变量为多条校验规则不包含名字或者错误信息定义
for key, rule := range checkRules {
value = nil
if v, ok := params[key]; ok {
value = v
}
if e := Check(value, rule, customMsgs[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
}