improve package gvalid for custom rule

This commit is contained in:
John 2020-09-21 23:51:30 +08:00
parent 1671391195
commit a29aef7e1c
8 changed files with 133 additions and 18 deletions

View File

@ -124,9 +124,9 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
// It converts value to string and then does the validation.
var (
// Do not trim it as the space is also part of the value.
val = gconv.String(value)
data = make(map[string]string)
errorMsgs = make(map[string]string)
val = gconv.String(value)
data = make(map[string]string)
errorMsgArray = make(map[string]string)
)
if len(params) > 0 {
for k, v := range gconv.Map(params[0]) {
@ -198,7 +198,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
"min-length",
"max-length":
if msg := checkLength(val, ruleKey, ruleVal, customMsgMap); msg != "" {
errorMsgs[ruleKey] = msg
errorMsgArray[ruleKey] = msg
} else {
match = true
}
@ -209,7 +209,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
"max",
"between":
if msg := checkRange(val, ruleKey, ruleVal, customMsgMap); msg != "" {
errorMsgs[ruleKey] = msg
errorMsgArray[ruleKey] = msg
} else {
match = true
}
@ -246,7 +246,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
var msg string
msg = getErrorMessageByRule(ruleKey, customMsgMap)
msg = strings.Replace(msg, ":format", ruleVal, -1)
errorMsgs[ruleKey] = msg
errorMsgArray[ruleKey] = msg
}
// Values of two fields should be equal as string.
@ -260,7 +260,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
var msg string
msg = getErrorMessageByRule(ruleKey, customMsgMap)
msg = strings.Replace(msg, ":field", ruleVal, -1)
errorMsgs[ruleKey] = msg
errorMsgArray[ruleKey] = msg
}
// Values of two fields should not be equal as string.
@ -275,7 +275,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
var msg string
msg = getErrorMessageByRule(ruleKey, customMsgMap)
msg = strings.Replace(msg, ":field", ruleVal, -1)
errorMsgs[ruleKey] = msg
errorMsgArray[ruleKey] = msg
}
// Field value should be in range of.
@ -451,6 +451,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
match = gregex.IsMatchString(`^([0-9A-Fa-f]{2}[\-:]){5}[0-9A-Fa-f]{2}$`, val)
default:
// Custom validation rules.
if f, ok := customRuleFuncMap[ruleKey]; ok {
var (
dataMap map[string]interface{}
@ -461,12 +462,12 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
}
if err := f(value, message, dataMap); err != nil {
match = false
errorMsgs[ruleKey] = err.Error()
errorMsgArray[ruleKey] = err.Error()
} else {
match = true
}
} else {
errorMsgs[ruleKey] = "Invalid rule name: " + ruleKey
errorMsgArray[ruleKey] = "Invalid rule name: " + ruleKey
}
}
@ -474,15 +475,15 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
if !match {
// It does nothing if the error message for this rule
// is already set in previous validation.
if _, ok := errorMsgs[ruleKey]; !ok {
errorMsgs[ruleKey] = getErrorMessageByRule(ruleKey, customMsgMap)
if _, ok := errorMsgArray[ruleKey]; !ok {
errorMsgArray[ruleKey] = getErrorMessageByRule(ruleKey, customMsgMap)
}
}
index++
}
if len(errorMsgs) > 0 {
if len(errorMsgArray) > 0 {
return newError([]string{rules}, ErrorMap{
key: errorMsgs,
key: errorMsgArray,
})
}
return nil

View File

@ -13,7 +13,8 @@ import (
// RuleFunc is the custom function for data validation.
// The parameter <value> specifies the value for this rule to validate.
// The parameter <message> specifies the custom error message or configured i18n message for this rule.
// The parameter <params> specifies all the parameters that needs .
// The parameter <params> specifies all the parameters that needs. You can ignore parameter <params> if
// you do not really need it in your custom validation rule.
type RuleFunc func(value interface{}, message string, params map[string]interface{}) error
var (

View File

@ -49,17 +49,26 @@ func newErrorStr(key, err string) *Error {
// Map returns the first error message as map.
func (e *Error) Map() map[string]string {
if e == nil {
return map[string]string{}
}
_, m := e.FirstItem()
return m
}
// Maps returns all error messages as map.
func (e *Error) Maps() ErrorMap {
if e == nil {
return nil
}
return e.errors
}
// FirstItem returns the field name and error messages for the first validation rule error.
func (e *Error) FirstItem() (key string, messages map[string]string) {
if e == nil {
return "", map[string]string{}
}
if e.firstItem != nil {
return e.firstKey, e.firstItem
}
@ -85,6 +94,9 @@ func (e *Error) FirstItem() (key string, messages map[string]string) {
// FirstRule returns the first error rule and message string.
func (e *Error) FirstRule() (rule string, err string) {
if e == nil {
return "", ""
}
// By sequence.
if len(e.rules) > 0 {
for _, v := range e.rules {
@ -112,22 +124,34 @@ func (e *Error) FirstRule() (rule string, err string) {
// FirstString returns the first error message as string.
// Note that the returned message might be different if it has no sequence.
func (e *Error) FirstString() (err string) {
if e == nil {
return ""
}
_, err = e.FirstRule()
return
}
// String returns all error messages as string, multiple error messages joined using char ';'.
func (e *Error) String() string {
if e == nil {
return ""
}
return strings.Join(e.Strings(), "; ")
}
// Error implements interface of error.Error.
func (e *Error) Error() string {
if e == nil {
return ""
}
return e.String()
}
// Strings returns all error messages as string array.
func (e *Error) Strings() (errs []string) {
if e == nil {
return []string{}
}
errs = make([]string, 0)
// By sequence.
if len(e.rules) > 0 {

View File

@ -57,6 +57,7 @@ var defaultMessages = map[string]string{
"in": "The :attribute value is not in acceptable range",
"not-in": "The :attribute value is not in acceptable range",
"regex": "The :attribute value is invalid",
"__default__": "The :attribute value is invalid",
}
// getErrorMessageByRule retrieves and returns the error message for specified rule.
@ -71,5 +72,12 @@ func getErrorMessageByRule(ruleKey string, customMsgMap map[string]string) strin
if content == "" {
content = defaultMessages[ruleKey]
}
// If there's no configured rule message, it uses default one.
if content == "" {
content = gi18n.GetContent(`gf.gvalid.rule.__default__`)
if content == "" {
content = defaultMessages["__default__"]
}
}
return content
}

View File

@ -16,7 +16,7 @@ import (
"github.com/gogf/gf/util/gvalid"
)
func Test_CustomRule(t *testing.T) {
func Test_CustomRule1(t *testing.T) {
rule := "custom"
err := gvalid.RegisterRule(rule, func(value interface{}, message string, params map[string]interface{}) error {
pass := gconv.String(value)
@ -62,3 +62,47 @@ func Test_CustomRule(t *testing.T) {
t.Assert(err, nil)
})
}
func Test_CustomRule2(t *testing.T) {
rule := "required-map"
err := gvalid.RegisterRule(rule, func(value interface{}, message string, params map[string]interface{}) error {
m := gconv.Map(value)
if len(m) == 0 {
return errors.New(message)
}
return nil
})
gtest.Assert(err, nil)
// Check.
gtest.C(t, func(t *gtest.T) {
errStr := "data map should not be empty"
t.Assert(gvalid.Check(g.Map{}, rule, errStr).String(), errStr)
t.Assert(gvalid.Check(g.Map{"k": "v"}, rule, errStr).String(), nil)
})
// Error with struct validation.
gtest.C(t, func(t *gtest.T) {
type T struct {
Value map[string]string `v:"uid@required-map#自定义错误"`
Data string `p:"data"`
}
st := &T{
Value: map[string]string{},
Data: "123456",
}
err := gvalid.CheckStruct(st, nil)
t.Assert(err.String(), "自定义错误")
})
// No error with struct validation.
gtest.C(t, func(t *gtest.T) {
type T struct {
Value map[string]string `v:"uid@required-map#自定义错误"`
Data string `p:"data"`
}
st := &T{
Value: map[string]string{"k": "v"},
Data: "123456",
}
err := gvalid.CheckStruct(st, nil)
t.Assert(err, nil)
})
}

View File

@ -7,8 +7,11 @@
package gvalid_test
import (
"errors"
"fmt"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/gvalid"
)
@ -106,3 +109,35 @@ func ExampleCheckStruct3() {
// Output:
// project id must between 1, 10000
}
func ExampleRegisterRule() {
rule := "unique-name"
gvalid.RegisterRule(rule, func(value interface{}, message string, params map[string]interface{}) error {
var (
id = gconv.Int(params["Id"])
name = gconv.String(value)
)
n, err := g.Table("user").Where("id != ? and name = ?", id, name).Count()
if err != nil {
return err
}
if n > 0 {
return errors.New(message)
}
return nil
})
type User struct {
Id int
Name string `v:"required|unique-name # 请输入用户名称|用户名称已被占用"`
Pass string `v:"required|length:6,18"`
}
user := &User{
Id: 1,
Name: "john",
Pass: "123456",
}
err := gvalid.CheckStruct(user, nil)
fmt.Println(err.Error())
// Output:
// 用户名称已被占用
}

View File

@ -40,4 +40,5 @@
"gf.gvalid.rule.different" = ":attribute 字段值不能与:field相同"
"gf.gvalid.rule.in" = ":attribute 字段值不合法"
"gf.gvalid.rule.not-in" = ":attribute 字段值不合法"
"gf.gvalid.rule.regex" = ":attribute 字段值不合法"
"gf.gvalid.rule.regex" = ":attribute 字段值不合法"
"gf.gvalid.rule.__default__" = ":attribute 字段值不合法"

View File

@ -40,4 +40,5 @@
"gf.gvalid.rule.different" = "The :attribute value must be different from field :field"
"gf.gvalid.rule.in" = "The :attribute value is not in acceptable range"
"gf.gvalid.rule.not-in" = "The :attribute value is not in acceptable range"
"gf.gvalid.rule.regex" = "The :attribute value is invalid"
"gf.gvalid.rule.regex" = "The :attribute value is invalid"
"gf.gvalid.rule.__default__" = "The :attribute value is invalid"