add context for validation functions of package gvalid

This commit is contained in:
John Guo
2021-10-11 21:34:19 +08:00
parent bc735fee51
commit bea54b445e
17 changed files with 243 additions and 235 deletions

View File

@ -4,11 +4,126 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package guid provides simple and high performance unique id generation functionality.
//
// Unique String ID:
// PLEASE VERY NOTE:
// This package only provides unique number generation for simple, convenient and most common
// usage purpose, but does not provide strict global unique number generation. Please refer
// to UUID algorithm for global unique number generation if necessary.
package guid
import (
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/encoding/ghash"
"github.com/gogf/gf/net/gipv4"
"github.com/gogf/gf/util/grand"
"os"
"strconv"
"time"
)
const (
sequenceMax = uint32(46655) // Sequence max("zzz").
randomStrBase = "0123456789abcdefghijklmnopqrstuvwxyz" // Random chars string(36 bytes).
)
var (
sequence gtype.Uint32 // Sequence for unique purpose of current process.
macAddrStr = "0000000" // MAC addresses hash result in 7 bytes.
processIdStr = "0000" // Process id in 4 bytes.
)
// init initializes several fixed local variable.
func init() {
// MAC addresses hash result in 7 bytes.
macs, _ := gipv4.GetMacArray()
if len(macs) > 0 {
var macAddrBytes []byte
for _, mac := range macs {
macAddrBytes = append(macAddrBytes, []byte(mac)...)
}
b := []byte{'0', '0', '0', '0', '0', '0', '0'}
s := strconv.FormatUint(uint64(ghash.DJBHash(macAddrBytes)), 36)
copy(b, s)
macAddrStr = string(b)
}
// Process id in 4 bytes.
{
b := []byte{'0', '0', '0', '0'}
s := strconv.FormatInt(int64(os.Getpid()), 36)
copy(b, s)
processIdStr = string(b)
}
}
// S creates and returns a global unique string in 32 bytes that meets most common
// usages without strict UUID algorithm. It returns a unique string using default
// unique algorithm if no `data` is given.
//
// The specified `data` can be no more than 2 parts. No matter how long each of the
// `data` size is, each of them will be hashed into 7 bytes as part of the result.
// If given `data` parts is less than 2, the leftover size of the result bytes will
// be token by random string.
//
// The returned string is composed with:
// 1. Default: MAC(7) + PID(4) + TimestampNano(12) + Sequence(3) + RandomString(6)
// 2. CustomData: Data(7/14) + TimestampNano(12) + Sequence(3) + RandomString(3/10)
//
// Note that
// 1. The returned length is fixed to 32 bytes for performance purpose.
// 2. The custom parameter `data` composed should have unique attribute in your
// business situation.
func S(data ...[]byte) string {
var (
b = make([]byte, 32)
nanoStr = strconv.FormatInt(time.Now().UnixNano(), 36)
)
if len(data) == 0 {
copy(b, macAddrStr)
copy(b[7:], processIdStr)
copy(b[11:], nanoStr)
copy(b[23:], getSequence())
copy(b[26:], getRandomStr(6))
} else if len(data) <= 2 {
n := 0
for i, v := range data {
// Ignore empty data item bytes.
if len(v) > 0 {
copy(b[i*7:], getDataHashStr(v))
n += 7
}
}
copy(b[n:], nanoStr)
copy(b[n+12:], getSequence())
copy(b[n+12+3:], getRandomStr(32-n-12-3))
} else {
panic("too many data parts, it should be no more than 2 parts")
}
return string(b)
}
// getSequence increases and returns the sequence string in 3 bytes.
// The sequence is less than "zzz"(46655).
func getSequence() []byte {
b := []byte{'0', '0', '0'}
s := strconv.FormatUint(uint64(sequence.Add(1)%sequenceMax), 36)
copy(b, s)
return b
}
// getRandomStr randomly picks and returns `n` count of chars from randomStrBase.
func getRandomStr(n int) []byte {
if n <= 0 {
return []byte{}
}
var (
b = make([]byte, n)
numberBytes = grand.B(n)
)
for i := range b {
b[i] = randomStrBase[numberBytes[i]%36]
}
return b
}
// getDataHashStr creates and returns hash bytes in 7 bytes with given data bytes.
func getDataHashStr(data []byte) []byte {
b := []byte{'0', '0', '0', '0', '0', '0', '0'}
s := strconv.FormatUint(uint64(ghash.DJBHash(data)), 36)
copy(b, s)
return b
}

View File

@ -1,129 +0,0 @@
// Copyright GoFrame Author(https://goframe.org). 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 guid
import (
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/encoding/ghash"
"github.com/gogf/gf/net/gipv4"
"github.com/gogf/gf/util/grand"
"os"
"strconv"
"time"
)
const (
sequenceMax = uint32(46655) // Sequence max("zzz").
randomStrBase = "0123456789abcdefghijklmnopqrstuvwxyz" // Random chars string(36 bytes).
)
var (
sequence gtype.Uint32 // Sequence for unique purpose of current process.
macAddrStr = "0000000" // MAC addresses hash result in 7 bytes.
processIdStr = "0000" // Process id in 4 bytes.
)
// init initializes several fixed local variable.
func init() {
// MAC addresses hash result in 7 bytes.
macs, _ := gipv4.GetMacArray()
if len(macs) > 0 {
var macAddrBytes []byte
for _, mac := range macs {
macAddrBytes = append(macAddrBytes, []byte(mac)...)
}
b := []byte{'0', '0', '0', '0', '0', '0', '0'}
s := strconv.FormatUint(uint64(ghash.DJBHash(macAddrBytes)), 36)
copy(b, s)
macAddrStr = string(b)
}
// Process id in 4 bytes.
{
b := []byte{'0', '0', '0', '0'}
s := strconv.FormatInt(int64(os.Getpid()), 36)
copy(b, s)
processIdStr = string(b)
}
}
// S creates and returns a global unique string in 32 bytes that meets most common
// usages without strict UUID algorithm. It returns an unique string using default
// unique algorithm if no `data` is given.
//
// The specified `data` can be no more than 2 parts. No matter how long each of the
// `data` size is, each of them will be hashed into 7 bytes as part of the result.
// If given `data` parts is less than 2, the leftover size of the result bytes will
// be token by random string.
//
// The returned string is composed with:
// 1. Default: MAC(7) + PID(4) + TimestampNano(12) + Sequence(3) + RandomString(6)
// 2. CustomData: Data(7/14) + TimestampNano(12) + Sequence(3) + RandomString(3/10)
//
// Note that
// 1. The returned length is fixed to 32 bytes for performance purpose.
// 2. The custom parameter `data` composed should have unique attribute in your
// business situation.
func S(data ...[]byte) string {
var (
b = make([]byte, 32)
nanoStr = strconv.FormatInt(time.Now().UnixNano(), 36)
)
if len(data) == 0 {
copy(b, macAddrStr)
copy(b[7:], processIdStr)
copy(b[11:], nanoStr)
copy(b[23:], getSequence())
copy(b[26:], getRandomStr(6))
} else if len(data) <= 2 {
n := 0
for i, v := range data {
// Ignore empty data item bytes.
if len(v) > 0 {
copy(b[i*7:], getDataHashStr(v))
n += 7
}
}
copy(b[n:], nanoStr)
copy(b[n+12:], getSequence())
copy(b[n+12+3:], getRandomStr(32-n-12-3))
} else {
panic("too many data parts, it should be no more than 2 parts")
}
return string(b)
}
// getSequence increases and returns the sequence string in 3 bytes.
// The sequence is less than "zzz"(46655).
func getSequence() []byte {
b := []byte{'0', '0', '0'}
s := strconv.FormatUint(uint64(sequence.Add(1)%sequenceMax), 36)
copy(b, s)
return b
}
// getRandomStr randomly picks and returns `n` count of chars from randomStrBase.
func getRandomStr(n int) []byte {
if n <= 0 {
return []byte{}
}
var (
b = make([]byte, n)
numberBytes = grand.B(n)
)
for i := range b {
b[i] = randomStrBase[numberBytes[i]%36]
}
return b
}
// getDataHashStr creates and returns hash bytes in 7 bytes with given data bytes.
func getDataHashStr(data []byte) []byte {
b := []byte{'0', '0', '0', '0', '0', '0', '0'}
s := strconv.FormatUint(uint64(ghash.DJBHash(data)), 36)
copy(b, s)
return b
}

View File

@ -253,7 +253,7 @@ func CheckValue(ctx context.Context, value interface{}, rules string, messages i
if len(params) > 0 {
data = params[0]
}
return defaultValidator.Ctx(ctx).Rules(rules).Data(data).Messages(messages).CheckValue(value)
return defaultValidator.Rules(rules).Data(data).Messages(messages).CheckValue(ctx, value)
}
// CheckMap validates map and returns the error result. It returns nil if with successful validation.
@ -266,7 +266,7 @@ func CheckMap(ctx context.Context, params interface{}, rules interface{}, messag
if len(messages) > 0 {
customErrorMessages = messages[0]
}
return defaultValidator.Ctx(ctx).Rules(rules).Messages(customErrorMessages).CheckMap(params)
return defaultValidator.Rules(rules).Messages(customErrorMessages).CheckMap(ctx, params)
}
// CheckStruct validates struct and returns the error result.
@ -280,7 +280,7 @@ func CheckStruct(ctx context.Context, object interface{}, rules interface{}, mes
if len(messages) > 0 {
customErrorMessages = messages[0]
}
return defaultValidator.Ctx(ctx).Rules(rules).Messages(customErrorMessages).CheckStruct(object)
return defaultValidator.Rules(rules).Messages(customErrorMessages).CheckStruct(ctx, object)
}
// CheckStructWithData validates struct with given parameter map and returns the error result.
@ -294,7 +294,7 @@ func CheckStructWithData(ctx context.Context, object interface{}, data interface
if len(messages) > 0 {
customErrorMessages = messages[0]
}
return defaultValidator.Ctx(ctx).Data(data).Rules(rules).Messages(customErrorMessages).CheckStruct(object)
return defaultValidator.Data(data).Rules(rules).Messages(customErrorMessages).CheckStruct(ctx, object)
}
// parseSequenceTag parses one sequence tag to field, rule and error message.

View File

@ -9,6 +9,7 @@ package gvalid
import "context"
// RuleFunc is the custom function for data validation.
//
// The parameter `rule` specifies the validation rule string, like "required", "between:1,100", etc.
// 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.

View File

@ -182,7 +182,7 @@ func (e *validationError) FirstString() (err string) {
return
}
// Current is alis of FirstString, which implements interface gerror.ApiCurrent.
// Current is alis of FirstString, which implements interface gerror.iCurrent.
func (e *validationError) Current() error {
if e == nil {
return nil

View File

@ -7,13 +7,11 @@
package gvalid
import (
"context"
"github.com/gogf/gf/i18n/gi18n"
)
// Validator is the validation manager for chaining operations.
type Validator struct {
ctx context.Context // Context containing custom context variables.
i18nManager *gi18n.Manager // I18n manager for error message translation.
key string // Single validation key.
value interface{} // Single validation value.
@ -28,7 +26,6 @@ type Validator struct {
// New creates and returns a new Validator.
func New() *Validator {
return &Validator{
ctx: context.TODO(), // Initialize an empty context.
i18nManager: gi18n.Instance(), // Use default i18n manager.
ruleFuncMap: make(map[string]RuleFunc), // Custom rule function storing map.
}
@ -48,13 +45,6 @@ func (v *Validator) I18n(i18nManager *gi18n.Manager) *Validator {
return newValidator
}
// Ctx is a chaining operation function, which sets the context for next validation.
func (v *Validator) Ctx(ctx context.Context) *Validator {
newValidator := v.Clone()
newValidator.ctx = ctx
return newValidator
}
// Bail sets the mark for stopping validation after the first validation error.
func (v *Validator) Bail() *Validator {
newValidator := v.Clone()

View File

@ -7,6 +7,7 @@
package gvalid
import (
"context"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/util/gconv"
"strings"
@ -14,11 +15,11 @@ import (
// CheckMap validates map and returns the error result. It returns nil if with successful validation.
// The parameter `params` should be type of map.
func (v *Validator) CheckMap(params interface{}) Error {
return v.doCheckMap(params)
func (v *Validator) CheckMap(ctx context.Context, params interface{}) Error {
return v.doCheckMap(ctx, params)
}
func (v *Validator) doCheckMap(params interface{}) Error {
func (v *Validator) doCheckMap(ctx context.Context, params interface{}) Error {
// If there's no validation rules, it does nothing and returns quickly.
if params == nil || v.rules == nil {
return nil
@ -105,7 +106,7 @@ func (v *Validator) doCheckMap(params interface{}) Error {
value = valueItem
}
// It checks each rule and its value in loop.
if validatedError := v.doCheckValue(doCheckValueInput{
if validatedError := v.doCheckValue(ctx, doCheckValueInput{
Name: checkRuleItem.Name,
Value: value,
Rule: checkRuleItem.Rule,

View File

@ -7,6 +7,7 @@
package gvalid
import (
"context"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/internal/structs"
"github.com/gogf/gf/util/gconv"
@ -16,14 +17,14 @@ import (
// CheckStruct validates struct and returns the error result.
// The parameter `object` should be type of struct/*struct.
func (v *Validator) CheckStruct(object interface{}) Error {
return v.doCheckStruct(object)
func (v *Validator) CheckStruct(ctx context.Context, object interface{}) Error {
return v.doCheckStruct(ctx, object)
}
func (v *Validator) doCheckStruct(object interface{}) Error {
func (v *Validator) doCheckStruct(ctx context.Context, object interface{}) Error {
var (
errorMaps = make(map[string]map[string]string) // Returning error.
fieldToAliasNameMap = make(map[string]string) // Field name to alias name map.
fieldToAliasNameMap = make(map[string]string) // Field names to alias name map.
)
fieldMap, err := structs.FieldMap(structs.FieldMapInput{
Pointer: object,
@ -33,7 +34,7 @@ func (v *Validator) doCheckStruct(object interface{}) Error {
if err != nil {
return newErrorStr(internalObjectErrRuleName, err.Error())
}
// It checks the struct recursively the its attribute is an embedded struct.
// It checks the struct recursively if its attribute is an embedded struct.
for _, field := range fieldMap {
if field.IsEmbedded() {
// No validation interface implements check.
@ -43,7 +44,7 @@ func (v *Validator) doCheckStruct(object interface{}) Error {
if _, ok := field.TagLookup(noValidationTagName); ok {
continue
}
if err := v.doCheckStruct(field.Value); err != nil {
if err := v.doCheckStruct(ctx, field.Value); err != nil {
// It merges the errors into single error map.
for k, m := range err.(*validationError).errors {
errorMaps[k] = m
@ -244,7 +245,7 @@ func (v *Validator) doCheckStruct(object interface{}) Error {
for _, checkRuleItem := range checkRules {
_, value = gutil.MapPossibleItemByKey(inputParamMap, checkRuleItem.Name)
// It checks each rule and its value in loop.
if validatedError := v.doCheckValue(doCheckValueInput{
if validatedError := v.doCheckValue(ctx, doCheckValueInput{
Name: checkRuleItem.Name,
Value: value,
Rule: checkRuleItem.Rule,

View File

@ -7,6 +7,7 @@
package gvalid
import (
"context"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/text/gstr"
@ -30,8 +31,8 @@ type iTime interface {
// CheckValue checks single value with specified rules.
// It returns nil if successful validation.
func (v *Validator) CheckValue(value interface{}) Error {
return v.doCheckValue(doCheckValueInput{
func (v *Validator) CheckValue(ctx context.Context, value interface{}) Error {
return v.doCheckValue(ctx, doCheckValueInput{
Name: "",
Value: value,
Rule: gconv.String(v.rules),
@ -51,7 +52,7 @@ type doCheckValueInput struct {
}
// doCheckSingleValue does the really rules validation for single key-value.
func (v *Validator) doCheckValue(input doCheckValueInput) Error {
func (v *Validator) doCheckValue(ctx context.Context, input doCheckValueInput) Error {
// If there's no validation rules, it does nothing and returns quickly.
if input.Rule == "" {
return nil
@ -131,8 +132,8 @@ func (v *Validator) doCheckValue(input doCheckValueInput) Error {
customRuleFunc = v.getRuleFunc(ruleKey)
if customRuleFunc != nil {
// It checks custom validation rules with most priority.
message := v.getErrorMessageByRule(ruleKey, customMsgMap)
if err := customRuleFunc(v.ctx, ruleItems[index], input.Value, message, input.DataRaw); err != nil {
message := v.getErrorMessageByRule(ctx, ruleKey, customMsgMap)
if err := customRuleFunc(ctx, ruleItems[index], input.Value, message, input.DataRaw); err != nil {
match = false
errorMsgArray[ruleKey] = err.Error()
} else {
@ -140,15 +141,18 @@ func (v *Validator) doCheckValue(input doCheckValueInput) Error {
}
} else {
// It checks build-in validation rules if there's no custom rule.
match, err = v.doCheckBuildInRules(doCheckBuildInRulesInput{
Index: index,
Value: input.Value,
RuleKey: ruleKey,
RulePattern: rulePattern,
RuleItems: ruleItems,
DataMap: input.DataMap,
CustomMsgMap: customMsgMap,
})
match, err = v.doCheckBuildInRules(
ctx,
doCheckBuildInRulesInput{
Index: index,
Value: input.Value,
RuleKey: ruleKey,
RulePattern: rulePattern,
RuleItems: ruleItems,
DataMap: input.DataMap,
CustomMsgMap: customMsgMap,
},
)
if !match && err != nil {
errorMsgArray[ruleKey] = err.Error()
}
@ -159,7 +163,7 @@ func (v *Validator) doCheckValue(input doCheckValueInput) Error {
// It does nothing if the error message for this rule
// is already set in previous validation.
if _, ok := errorMsgArray[ruleKey]; !ok {
errorMsgArray[ruleKey] = v.getErrorMessageByRule(ruleKey, customMsgMap)
errorMsgArray[ruleKey] = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap)
}
// If it is with error and there's bail rule,
// it then does not continue validating for left rules.
@ -170,9 +174,13 @@ func (v *Validator) doCheckValue(input doCheckValueInput) Error {
index++
}
if len(errorMsgArray) > 0 {
return newError(gcode.CodeValidationFailed, []fieldRule{{Name: input.Name, Rule: input.Rule}}, map[string]map[string]string{
input.Name: errorMsgArray,
})
return newError(
gcode.CodeValidationFailed,
[]fieldRule{{Name: input.Name, Rule: input.Rule}},
map[string]map[string]string{
input.Name: errorMsgArray,
},
)
}
return nil
}
@ -187,7 +195,7 @@ type doCheckBuildInRulesInput struct {
CustomMsgMap map[string]string
}
func (v *Validator) doCheckBuildInRules(input doCheckBuildInRulesInput) (match bool, err error) {
func (v *Validator) doCheckBuildInRules(ctx context.Context, input doCheckBuildInRulesInput) (match bool, err error) {
valueStr := gconv.String(input.Value)
switch input.RuleKey {
// Required rules.
@ -208,7 +216,7 @@ func (v *Validator) doCheckBuildInRules(input doCheckBuildInRulesInput) (match b
"min-length",
"max-length",
"size":
if msg := v.checkLength(valueStr, input.RuleKey, input.RulePattern, input.CustomMsgMap); msg != "" {
if msg := v.checkLength(ctx, valueStr, input.RuleKey, input.RulePattern, input.CustomMsgMap); msg != "" {
return match, gerror.NewOption(gerror.Option{
Text: msg,
Code: gcode.CodeValidationFailed,
@ -222,7 +230,7 @@ func (v *Validator) doCheckBuildInRules(input doCheckBuildInRulesInput) (match b
"min",
"max",
"between":
if msg := v.checkRange(valueStr, input.RuleKey, input.RulePattern, input.CustomMsgMap); msg != "" {
if msg := v.checkRange(ctx, valueStr, input.RuleKey, input.RulePattern, input.CustomMsgMap); msg != "" {
return match, gerror.NewOption(gerror.Option{
Text: msg,
Code: gcode.CodeValidationFailed,
@ -260,7 +268,7 @@ func (v *Validator) doCheckBuildInRules(input doCheckBuildInRulesInput) (match b
match = true
} else {
var msg string
msg = v.getErrorMessageByRule(input.RuleKey, input.CustomMsgMap)
msg = v.getErrorMessageByRule(ctx, input.RuleKey, input.CustomMsgMap)
msg = strings.Replace(msg, ":format", input.RulePattern, -1)
return match, gerror.NewOption(gerror.Option{
Text: msg,
@ -278,7 +286,7 @@ func (v *Validator) doCheckBuildInRules(input doCheckBuildInRulesInput) (match b
}
if !match {
var msg string
msg = v.getErrorMessageByRule(input.RuleKey, input.CustomMsgMap)
msg = v.getErrorMessageByRule(ctx, input.RuleKey, input.CustomMsgMap)
msg = strings.Replace(msg, ":field", input.RulePattern, -1)
return match, gerror.NewOption(gerror.Option{
Text: msg,
@ -297,7 +305,7 @@ func (v *Validator) doCheckBuildInRules(input doCheckBuildInRulesInput) (match b
}
if !match {
var msg string
msg = v.getErrorMessageByRule(input.RuleKey, input.CustomMsgMap)
msg = v.getErrorMessageByRule(ctx, input.RuleKey, input.CustomMsgMap)
msg = strings.Replace(msg, ":field", input.RulePattern, -1)
return match, gerror.NewOption(gerror.Option{
Text: msg,

View File

@ -6,27 +6,29 @@
package gvalid
import "context"
// getErrorMessageByRule retrieves and returns the error message for specified rule.
// It firstly retrieves the message from custom message map, and then checks i18n manager,
// it returns the default error message if it's not found in custom message map or i18n manager.
func (v *Validator) getErrorMessageByRule(ruleKey string, customMsgMap map[string]string) string {
func (v *Validator) getErrorMessageByRule(ctx context.Context, ruleKey string, customMsgMap map[string]string) string {
content := customMsgMap[ruleKey]
if content != "" {
// I18n translation.
i18nContent := v.i18nManager.GetContent(v.ctx, content)
i18nContent := v.i18nManager.GetContent(ctx, content)
if i18nContent != "" {
return i18nContent
}
return content
}
// Retrieve default message according to certain rule.
content = v.i18nManager.GetContent(v.ctx, ruleMessagePrefixForI18n+ruleKey)
content = v.i18nManager.GetContent(ctx, ruleMessagePrefixForI18n+ruleKey)
if content == "" {
content = defaultMessages[ruleKey]
}
// If there's no configured rule message, it uses default one.
if content == "" {
content = v.i18nManager.GetContent(v.ctx, ruleMessagePrefixForI18n+internalDefaultRuleName)
content = v.i18nManager.GetContent(ctx, ruleMessagePrefixForI18n+internalDefaultRuleName)
if content == "" {
content = defaultMessages[internalDefaultRuleName]
}

View File

@ -7,6 +7,7 @@
package gvalid
import (
"context"
"github.com/gogf/gf/util/gconv"
"strconv"
"strings"
@ -15,7 +16,7 @@ import (
// checkLength checks `value` using length rules.
// The length is calculated using unicode string, which means one chinese character or letter
// both has the length of 1.
func (v *Validator) checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
func (v *Validator) checkLength(ctx context.Context, value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
var (
msg = ""
runeArray = gconv.Runes(value)
@ -39,7 +40,7 @@ func (v *Validator) checkLength(value, ruleKey, ruleVal string, customMsgMap map
}
}
if valueLen < min || valueLen > max {
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap)
msg = strings.Replace(msg, ":min", strconv.Itoa(min), -1)
msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1)
return msg
@ -48,21 +49,21 @@ func (v *Validator) checkLength(value, ruleKey, ruleVal string, customMsgMap map
case "min-length":
min, err := strconv.Atoi(ruleVal)
if valueLen < min || err != nil {
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap)
msg = strings.Replace(msg, ":min", strconv.Itoa(min), -1)
}
case "max-length":
max, err := strconv.Atoi(ruleVal)
if valueLen > max || err != nil {
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap)
msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1)
}
case "size":
size, err := strconv.Atoi(ruleVal)
if valueLen != size || err != nil {
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap)
msg = strings.Replace(msg, ":size", strconv.Itoa(size), -1)
}
}

View File

@ -7,12 +7,13 @@
package gvalid
import (
"context"
"strconv"
"strings"
)
// checkRange checks `value` using range rules.
func (v *Validator) checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
func (v *Validator) checkRange(ctx context.Context, value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
msg := ""
switch ruleKey {
// Value range.
@ -32,7 +33,7 @@ func (v *Validator) checkRange(value, ruleKey, ruleVal string, customMsgMap map[
}
valueF, err := strconv.ParseFloat(value, 10)
if valueF < min || valueF > max || err != nil {
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap)
msg = strings.Replace(msg, ":min", strconv.FormatFloat(min, 'f', -1, 64), -1)
msg = strings.Replace(msg, ":max", strconv.FormatFloat(max, 'f', -1, 64), -1)
}
@ -44,7 +45,7 @@ func (v *Validator) checkRange(value, ruleKey, ruleVal string, customMsgMap map[
valueN, err2 = strconv.ParseFloat(value, 10)
)
if valueN < min || err1 != nil || err2 != nil {
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap)
msg = strings.Replace(msg, ":min", strconv.FormatFloat(min, 'f', -1, 64), -1)
}
@ -55,7 +56,7 @@ func (v *Validator) checkRange(value, ruleKey, ruleVal string, customMsgMap map[
valueN, err2 = strconv.ParseFloat(value, 10)
)
if valueN > max || err1 != nil || err2 != nil {
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap)
msg = strings.Replace(msg, ":max", strconv.FormatFloat(max, 'f', -1, 64), -1)
}

View File

@ -10,6 +10,7 @@ import (
"context"
"errors"
"fmt"
"github.com/gogf/gf/os/gctx"
"math"
"reflect"
@ -30,7 +31,7 @@ func ExampleCheckMap() {
"password@required|length:6,16|same:password2#密码不能为空|密码长度应当在:min到:max之间|两次密码输入不相等",
"password2@required|length:6,16#",
}
if e := gvalid.CheckMap(context.TODO(), params, rules); e != nil {
if e := gvalid.CheckMap(gctx.New(), params, rules); e != nil {
fmt.Println(e.Map())
fmt.Println(e.FirstItem())
fmt.Println(e.FirstString())
@ -52,7 +53,7 @@ func ExampleCheckMap2() {
"password@required|length:6,16|same:password2#密码不能为空|密码长度应当在:min到:max之间|两次密码输入不相等",
"password2@required|length:6,16#",
}
if e := gvalid.CheckMap(context.TODO(), params, rules); e != nil {
if e := gvalid.CheckMap(gctx.New(), params, rules); e != nil {
fmt.Println(e.Map())
fmt.Println(e.FirstItem())
fmt.Println(e.FirstString())
@ -74,7 +75,7 @@ func ExampleCheckStruct() {
Page: 1,
Size: 10,
}
err := gvalid.CheckStruct(context.TODO(), obj, nil)
err := gvalid.CheckStruct(gctx.New(), obj, nil)
fmt.Println(err == nil)
// Output:
// true
@ -91,7 +92,7 @@ func ExampleCheckStruct2() {
Page: 1,
Size: 10,
}
err := gvalid.CheckStruct(context.TODO(), obj, nil)
err := gvalid.CheckStruct(gctx.New(), obj, nil)
fmt.Println(err == nil)
// Output:
// true
@ -108,7 +109,7 @@ func ExampleCheckStruct3() {
Page: 1,
Size: 10,
}
err := gvalid.CheckStruct(context.TODO(), obj, nil)
err := gvalid.CheckStruct(gctx.New(), obj, nil)
fmt.Println(err)
// Output:
// project id must between 1, 10000
@ -141,7 +142,7 @@ func ExampleRegisterRule() {
}
return nil
})
err := gvalid.CheckStruct(context.TODO(), user, nil)
err := gvalid.CheckStruct(gctx.New(), user, nil)
fmt.Println(err.Error())
// May Output:
// 用户名称已被占用
@ -175,14 +176,14 @@ func ExampleRegisterRule_OverwriteRequired() {
}
return nil
})
fmt.Println(gvalid.CheckValue(context.TODO(), "", "required", "It's required"))
fmt.Println(gvalid.CheckValue(context.TODO(), 0, "required", "It's required"))
fmt.Println(gvalid.CheckValue(context.TODO(), false, "required", "It's required"))
fmt.Println(gvalid.CheckValue(gctx.New(), "", "required", "It's required"))
fmt.Println(gvalid.CheckValue(gctx.New(), 0, "required", "It's required"))
fmt.Println(gvalid.CheckValue(gctx.New(), false, "required", "It's required"))
gvalid.DeleteRule(rule)
fmt.Println("rule deleted")
fmt.Println(gvalid.CheckValue(context.TODO(), "", "required", "It's required"))
fmt.Println(gvalid.CheckValue(context.TODO(), 0, "required", "It's required"))
fmt.Println(gvalid.CheckValue(context.TODO(), false, "required", "It's required"))
fmt.Println(gvalid.CheckValue(gctx.New(), "", "required", "It's required"))
fmt.Println(gvalid.CheckValue(gctx.New(), 0, "required", "It's required"))
fmt.Println(gvalid.CheckValue(gctx.New(), false, "required", "It's required"))
// Output:
// It's required
// It's required
@ -197,7 +198,10 @@ func ExampleValidator_Rules() {
data := g.Map{
"password": "123",
}
err := g.Validator().Data(data).Rules("required-with:password").Messages("请输入确认密码").CheckValue("")
err := g.Validator().Data(data).
Rules("required-with:password").
Messages("请输入确认密码").
CheckValue(gctx.New(), "")
fmt.Println(err.String())
// Output:
@ -205,7 +209,9 @@ func ExampleValidator_Rules() {
}
func ExampleValidator_CheckValue() {
err := g.Validator().Rules("min:18").Messages("未成年人不允许注册哟").CheckValue(16)
err := g.Validator().Rules("min:18").
Messages("未成年人不允许注册哟").
CheckValue(gctx.New(), 16)
fmt.Println(err.String())
// Output:
@ -230,7 +236,10 @@ func ExampleValidator_CheckMap() {
"same": "两次密码输入不相等",
},
}
err := g.Validator().Messages(messages).Rules(rules).CheckMap(params)
err := g.Validator().
Messages(messages).
Rules(rules).
CheckMap(gctx.New(), params)
if err != nil {
g.Dump(err.Maps())
}
@ -259,7 +268,7 @@ func ExampleValidator_CheckStruct() {
if err := gconv.Scan(data, &user); err != nil {
panic(err)
}
err := g.Validator().Data(data).CheckStruct(user)
err := g.Validator().Data(data).CheckStruct(gctx.New(), user)
if err != nil {
fmt.Println(err.Items())
}

View File

@ -10,6 +10,7 @@ import (
"context"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/os/gctx"
"github.com/gogf/gf/os/gtime"
"testing"
"time"
@ -19,6 +20,10 @@ import (
"github.com/gogf/gf/util/gvalid"
)
var (
ctx = gctx.New()
)
func Test_Check(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
rule := "abc:6,16"
@ -1018,13 +1023,13 @@ func Test_InternalError_String(t *testing.T) {
func Test_Code(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
err := g.Validator().Rules("required").CheckValue("")
err := g.Validator().Rules("required").CheckValue(ctx, "")
t.AssertNE(err, nil)
t.Assert(gerror.Code(err), gcode.CodeValidationFailed)
})
gtest.C(t, func(t *gtest.T) {
err := g.Validator().Rules("none-exist-rule").CheckValue("")
err := g.Validator().Rules("none-exist-rule").CheckValue(ctx, "")
t.AssertNE(err, nil)
t.Assert(gerror.Code(err), gcode.CodeInternalError)
})
@ -1036,7 +1041,7 @@ func Test_Bail(t *testing.T) {
err := g.Validator().
Rules("required|min:1|between:1,100").
Messages("|min number is 1|size is between 1 and 100").
CheckValue(-1)
CheckValue(ctx, -1)
t.AssertNE(err, nil)
t.Assert(err.Error(), "min number is 1; size is between 1 and 100")
})
@ -1046,7 +1051,7 @@ func Test_Bail(t *testing.T) {
err := g.Validator().
Rules("bail|required|min:1|between:1,100").
Messages("||min number is 1|size is between 1 and 100").
CheckValue(-1)
CheckValue(ctx, -1)
t.AssertNE(err, nil)
t.Assert(err.Error(), "min number is 1")
})
@ -1061,7 +1066,7 @@ func Test_Bail(t *testing.T) {
Page: 1,
Size: -1,
}
err := g.Validator().CheckStruct(obj)
err := g.Validator().CheckStruct(ctx, obj)
t.AssertNE(err, nil)
t.Assert(err.Error(), "min number is 1; size is between 1 and 100")
})
@ -1075,7 +1080,7 @@ func Test_Bail(t *testing.T) {
Page: 1,
Size: -1,
}
err := g.Validator().CheckStruct(obj)
err := g.Validator().CheckStruct(ctx, obj)
t.AssertNE(err, nil)
t.Assert(err.Error(), "min number is 1")
})

View File

@ -225,7 +225,7 @@ func Test_Map_Bail(t *testing.T) {
"password@required|length:6,16|same:password2#密码不能为空|密码长度应当在:min到:max之间|两次密码输入不相等",
"password2@required|length:6,16#",
}
err := g.Validator().Bail().Rules(rules).CheckMap(params)
err := g.Validator().Bail().Rules(rules).CheckMap(ctx, params)
t.AssertNE(err, nil)
t.Assert(err.String(), "账号不能为空; 账号长度应当在6到16之间")
})
@ -241,7 +241,7 @@ func Test_Map_Bail(t *testing.T) {
"password@required|length:6,16|same:password2#密码不能为空|密码长度应当在:min到:max之间|两次密码输入不相等",
"password2@required|length:6,16#",
}
err := g.Validator().Bail().Rules(rules).CheckMap(params)
err := g.Validator().Bail().Rules(rules).CheckMap(ctx, params)
t.AssertNE(err, nil)
t.Assert(err.String(), "账号不能为空")
})

View File

@ -171,14 +171,17 @@ func TestValidator_RuleFunc(t *testing.T) {
return nil
}
gtest.C(t, func(t *gtest.T) {
err := g.Validator().Rules(ruleName).Messages("custom message").RuleFunc(ruleName, ruleFunc).CheckValue("123456")
err := g.Validator().Rules(ruleName).
Messages("custom message").
RuleFunc(ruleName, ruleFunc).
CheckValue(ctx, "123456")
t.Assert(err.String(), "custom message")
err = g.Validator().
Rules(ruleName).
Messages("custom message").
Data(g.Map{"data": "123456"}).
RuleFunc(ruleName, ruleFunc).
CheckValue("123456")
CheckValue(ctx, "123456")
t.AssertNil(err)
})
// Error with struct validation.
@ -191,7 +194,7 @@ func TestValidator_RuleFunc(t *testing.T) {
Value: "123",
Data: "123456",
}
err := g.Validator().RuleFunc(ruleName, ruleFunc).CheckStruct(st)
err := g.Validator().RuleFunc(ruleName, ruleFunc).CheckStruct(ctx, st)
t.Assert(err.String(), "自定义错误")
})
// No error with struct validation.
@ -204,7 +207,7 @@ func TestValidator_RuleFunc(t *testing.T) {
Value: "123456",
Data: "123456",
}
err := g.Validator().RuleFunc(ruleName, ruleFunc).CheckStruct(st)
err := g.Validator().RuleFunc(ruleName, ruleFunc).CheckStruct(ctx, st)
t.AssertNil(err)
})
}
@ -227,7 +230,7 @@ func TestValidator_RuleFuncMap(t *testing.T) {
Messages("custom message").
RuleFuncMap(map[string]gvalid.RuleFunc{
ruleName: ruleFunc,
}).CheckValue("123456")
}).CheckValue(ctx, "123456")
t.Assert(err.String(), "custom message")
err = g.Validator().
Rules(ruleName).
@ -236,7 +239,7 @@ func TestValidator_RuleFuncMap(t *testing.T) {
RuleFuncMap(map[string]gvalid.RuleFunc{
ruleName: ruleFunc,
}).
CheckValue("123456")
CheckValue(ctx, "123456")
t.AssertNil(err)
})
// Error with struct validation.
@ -252,7 +255,7 @@ func TestValidator_RuleFuncMap(t *testing.T) {
err := g.Validator().
RuleFuncMap(map[string]gvalid.RuleFunc{
ruleName: ruleFunc,
}).CheckStruct(st)
}).CheckStruct(ctx, st)
t.Assert(err.String(), "自定义错误")
})
// No error with struct validation.
@ -268,7 +271,7 @@ func TestValidator_RuleFuncMap(t *testing.T) {
err := g.Validator().
RuleFuncMap(map[string]gvalid.RuleFunc{
ruleName: ruleFunc,
}).CheckStruct(st)
}).CheckStruct(ctx, st)
t.AssertNil(err)
})
}

View File

@ -24,14 +24,14 @@ func TestValidator_I18n(t *testing.T) {
validator = gvalid.New().I18n(i18nManager)
)
gtest.C(t, func(t *gtest.T) {
err = validator.Rules("required").CheckValue("")
err = validator.Rules("required").CheckValue(ctx, "")
t.Assert(err.String(), "The field is required")
err = validator.Ctx(ctxCn).Rules("required").CheckValue("")
err = validator.Rules("required").CheckValue(ctxCn, "")
t.Assert(err.String(), "字段不能为空")
})
gtest.C(t, func(t *gtest.T) {
err = validator.Ctx(ctxCn).Rules("required").Messages("CustomMessage").CheckValue("")
err = validator.Rules("required").Messages("CustomMessage").CheckValue(ctxCn, "")
t.Assert(err.String(), "自定义错误")
})
gtest.C(t, func(t *gtest.T) {
@ -44,7 +44,7 @@ func TestValidator_I18n(t *testing.T) {
Page: 1,
Size: 10,
}
err := validator.Ctx(ctxCn).CheckStruct(obj)
err = validator.CheckStruct(ctxCn, obj)
t.Assert(err.String(), "项目ID必须大于等于1并且要小于等于10000")
})
}