加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
validation.go 13.31 KB
一键复制 编辑 原始数据 按行查看 历史
Oscar van Leusen 提交于 2022-08-26 23:29 . add golangci-lint linter
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
package validate
import (
"fmt"
"reflect"
"strings"
)
// some default value settings.
const (
fieldTag = "json"
filterTag = "filter"
labelTag = "label"
messageTag = "message"
validateTag = "validate"
filterError = "_filter"
validateError = "_validate"
// sniff Length, use for detect file mime type
sniffLen = 512
// 32 MB
defaultMaxMemory int64 = 32 << 20
)
// Validation definition
type Validation struct {
// for optimize create instance. refer go-playground/validator
// v *Validation
// pool *sync.Pool
// source input data
data DataFace
// all validated fields list
// fields []string
// filtered/validated safe data
safeData M
// filtered clean data
filteredData M
// Errors for validate
Errors Errors
// CacheKey for cache rules
// CacheKey string
// StopOnError If true: An error occurs, it will cease to continue to verify
StopOnError bool
// SkipOnEmpty Skip check on field not exist or value is empty
SkipOnEmpty bool
// UpdateSource Whether to update source field value, useful for struct validate
UpdateSource bool
// CheckDefault Whether to validate the default value set by the user
CheckDefault bool
// CachingRules switch. default is False
// CachingRules bool
// save user custom set default values
defValues map[string]interface{}
// mark has error occurs
hasError bool
// mark is filtered
hasFiltered bool
// mark is validated
hasValidated bool
// validate rules for the validation
rules []*Rule
// validators for the validation
validators map[string]int8
// validator func meta info
validatorMetas map[string]*funcMeta
// validator func reflect.Value map
validatorValues map[string]reflect.Value
// translator instance
trans *Translator
// current scene name
scene string
// scenes config.
// {
// "create": {"field0", "field1"}
// "update": {"field0", "field2"}
// }
scenes SValues
// should check fields in current scene.
sceneFields map[string]uint8
// filtering rules for the validation
filterRules []*FilterRule
// filter func reflect.Value map
filterValues map[string]reflect.Value
}
// NewEmpty new validation instance, but not add data.
func NewEmpty(scene ...string) *Validation {
return NewValidation(nil, scene...)
}
// NewValidation new validation instance
func NewValidation(data DataFace, scene ...string) *Validation {
return newValidation(data).SetScene(scene...)
}
/*************************************************************
* validation settings
*************************************************************/
// ResetResult reset the validate result.
func (v *Validation) ResetResult() {
v.Errors = Errors{}
v.hasError = false
v.hasFiltered = false
v.hasValidated = false
// result data
v.safeData = make(map[string]interface{})
v.filteredData = make(map[string]interface{})
}
// Reset the Validation instance.
//
// will reset
// - validate result
// - validate rules
// - validate filterRules
// - custom validators
func (v *Validation) Reset() {
v.ResetResult()
// rules
v.rules = v.rules[:0]
v.filterRules = v.filterRules[:0]
v.validators = make(map[string]int8)
}
// WithSelf config the Validation instance
func (v *Validation) WithSelf(fn func(v *Validation)) *Validation {
fn(v)
return v
}
// WithTrans with a custom translator
func (v *Validation) WithTrans(trans *Translator) *Validation {
v.trans = trans
return v
}
// WithScenarios is alias of the WithScenes()
func (v *Validation) WithScenarios(scenes SValues) *Validation {
return v.WithScenes(scenes)
}
// WithScenes set scene config.
//
// Usage:
//
// v.WithScenes(SValues{
// "create": []string{"name", "email"},
// "update": []string{"name"},
// })
// ok := v.AtScene("create").Validate()
func (v *Validation) WithScenes(scenes map[string][]string) *Validation {
v.scenes = scenes
return v
}
// AtScene setting current validate scene.
func (v *Validation) AtScene(scene string) *Validation {
v.scene = scene
return v
}
// InScene alias of the AtScene()
func (v *Validation) InScene(scene string) *Validation {
return v.AtScene(scene)
}
// SetScene alias of the AtScene()
func (v *Validation) SetScene(scene ...string) *Validation {
if len(scene) > 0 {
v.AtScene(scene[0])
}
return v
}
/*************************************************************
* add validators for validation
*************************************************************/
// AddValidators to the Validation
func (v *Validation) AddValidators(m map[string]interface{}) *Validation {
for name, checkFunc := range m {
v.AddValidator(name, checkFunc)
}
return v
}
// AddValidator to the Validation. checkFunc must return a bool.
// Usage:
//
// v.AddValidator("myFunc", func(val interface{}) bool {
// // do validate val ...
// return true
// })
func (v *Validation) AddValidator(name string, checkFunc interface{}) *Validation {
fv := checkValidatorFunc(name, checkFunc)
v.validators[name] = 2 // custom
v.validatorValues[name] = fv
v.validatorMetas[name] = newFuncMeta(name, false, fv)
return v
}
// ValidatorMeta get by name
func (v *Validation) validatorMeta(name string) *funcMeta {
// current validation
if fm, ok := v.validatorMetas[name]; ok {
return fm
}
// from global validators
if fm, ok := validatorMetas[name]; ok {
return fm
}
// if v.data is StructData instance.
if v.data.Type() == sourceStruct {
fv, ok := v.data.(*StructData).FuncValue(name)
if ok {
fm := newFuncMeta(name, false, fv)
// storage it.
v.validators[name] = 2 // custom
v.validatorMetas[name] = fm
return fm
}
}
return nil
}
// HasValidator check
func (v *Validation) HasValidator(name string) bool {
name = ValidatorName(name)
// current validation
if _, ok := v.validatorMetas[name]; ok {
return true
}
// global validators
_, ok := validatorMetas[name]
return ok
}
// Validators get all validator names
func (v *Validation) Validators(withGlobal bool) map[string]int8 {
if withGlobal {
mp := make(map[string]int8)
for name, typ := range validators {
mp[name] = typ
}
for name, typ := range v.validators {
mp[name] = typ
}
return mp
}
return v.validators
}
/*************************************************************
* Do filtering/sanitize
*************************************************************/
// Sanitize data by filter rules
func (v *Validation) Sanitize() bool {
return v.Filtering()
}
// Filtering data by filter rules
func (v *Validation) Filtering() bool {
if v.hasFiltered {
return v.IsSuccess()
}
// apply rule to validate data.
for _, rule := range v.filterRules {
if err := rule.Apply(v); err != nil { // has error
v.AddError(filterError, filterError, err.Error())
break
}
}
v.hasFiltered = true
return v.IsSuccess()
}
/*************************************************************
* errors messages
*************************************************************/
// WithTranslates settings. you can be custom field translates.
//
// Usage:
//
// v.WithTranslates(map[string]string{
// "name": "Username",
// "pwd": "Password",
// })
func (v *Validation) WithTranslates(m map[string]string) *Validation {
v.trans.AddLabelMap(m)
return v
}
// AddTranslates settings data. like WithTranslates()
func (v *Validation) AddTranslates(m map[string]string) {
v.trans.AddLabelMap(m)
}
// WithMessages settings. you can custom validator error messages.
//
// Usage:
//
// // key is "validator" or "field.validator"
// v.WithMessages(map[string]string{
// "require": "oh! {field} is required",
// "range": "oh! {field} must be in the range %d - %d",
// })
func (v *Validation) WithMessages(m map[string]string) *Validation {
v.trans.AddMessages(m)
return v
}
// AddMessages settings data. like WithMessages()
func (v *Validation) AddMessages(m map[string]string) {
v.trans.AddMessages(m)
}
// WithError add error of the validation
func (v *Validation) WithError(err error) *Validation {
if err != nil {
v.AddError(validateError, validateError, err.Error())
}
return v
}
// AddError message for a field
func (v *Validation) AddError(field, validator, msg string) {
if !v.hasError {
v.hasError = true
}
field = v.trans.FieldName(field)
v.Errors.Add(field, validator, msg)
}
// AddErrorf add a formatted error message
func (v *Validation) AddErrorf(field, msgFormat string, args ...interface{}) {
v.AddError(field, validateError, fmt.Sprintf(msgFormat, args...))
}
// Trans get translator
func (v *Validation) Trans() *Translator {
// if v.trans == nil {
// v.trans = StdTranslator
// }
return v.trans
}
func (v *Validation) convArgTypeError(field, name string, argKind, wantKind reflect.Kind, argIdx int) {
v.AddErrorf(field, "cannot convert %s to arg#%d(%s), validator '%s'", argKind, argIdx, wantKind, name)
}
/*************************************************************
* getter methods
*************************************************************/
// Raw value get by key
func (v *Validation) Raw(key string) (interface{}, bool) {
if v.data == nil { // check input data
return nil, false
}
return v.data.Get(key)
}
// RawVal value get by key
func (v *Validation) RawVal(key string) interface{} {
if v.data == nil { // check input data
return nil
}
val, _ := v.data.Get(key)
return val
}
// try to get value by key.
//
// If v.data is StructData, will return zero check
func (v *Validation) tryGet(key string) (val interface{}, exist, zero bool) {
if v.data == nil {
return
}
// if end withs: .*, return the parent value
key = strings.TrimSuffix(key, ".*")
// find from filtered data.
if val, ok := v.filteredData[key]; ok {
return val, true, false
}
// find from validated data. (such as has default value)
if val, ok := v.safeData[key]; ok {
return val, true, false
}
// TODO add cache data v.caches[key]
// get from source data
return v.data.TryGet(key)
}
// Get value by key.
func (v *Validation) Get(key string) (val interface{}, exist bool) {
val, exist, _ = v.tryGet(key)
return
}
// GetWithDefault get field value by key.
//
// On not found, if it has default value, will return default-value.
func (v *Validation) GetWithDefault(key string) (val interface{}, exist, isDefault bool) {
var zero bool
val, exist, zero = v.tryGet(key)
if exist && !zero {
return
}
// try read custom default value
defVal, isDefault := v.defValues[key]
if isDefault {
val = defVal
}
return
}
// Filtered get filtered value by key
func (v *Validation) Filtered(key string) interface{} {
return v.filteredData[key]
}
// Safe get safe value by key
func (v *Validation) Safe(key string) (val interface{}, ok bool) {
if v.data == nil { // check input data
return
}
val, ok = v.safeData[key]
return
}
// SafeVal get safe value by key
func (v *Validation) SafeVal(key string) interface{} {
val, _ := v.Safe(key)
return val
}
// GetSafe get safe value by key
func (v *Validation) GetSafe(key string) interface{} {
val, _ := v.Safe(key)
return val
}
// BindStruct binding safe data to an struct.
func (v *Validation) BindStruct(ptr interface{}) error {
return v.BindSafeData(ptr)
}
// BindSafeData binding safe data to an struct.
func (v *Validation) BindSafeData(ptr interface{}) error {
if len(v.safeData) == 0 { // no safe data.
return nil
}
// to json bytes
bts, err := Marshal(v.safeData)
if err != nil {
return err
}
return Unmarshal(bts, ptr)
}
// Set value by key
func (v *Validation) Set(field string, val interface{}) error {
// check input data
if v.data == nil {
return ErrEmptyData
}
_, err := v.data.Set(field, val)
return err
}
// only update set value by key for struct
func (v *Validation) updateValue(field string, val interface{}) (interface{}, error) {
// data source is struct
if v.data.Type() == sourceStruct {
return v.data.Set(field, val)
}
// TODO dont update value for Form and Map data source
return val, nil
}
// SetDefValue set a default value of given field
func (v *Validation) SetDefValue(field string, val interface{}) {
if v.defValues == nil {
v.defValues = make(map[string]interface{})
}
v.defValues[field] = val
}
// GetDefValue get default value of the field
func (v *Validation) GetDefValue(field string) (interface{}, bool) {
defVal, ok := v.defValues[field]
return defVal, ok
}
// SceneFields field names get
func (v *Validation) SceneFields() []string {
return v.scenes[v.scene]
}
// scene field name map build
func (v *Validation) sceneFieldMap() (m map[string]uint8) {
if v.scene == "" {
return
}
if fields, ok := v.scenes[v.scene]; ok {
m = make(map[string]uint8, len(fields))
for _, field := range fields {
m[field] = 1
}
}
return
}
// Scene name get for current validation
func (v *Validation) Scene() string {
return v.scene
}
// IsOK for the validating
func (v *Validation) IsOK() bool {
return !v.hasError
}
// IsFail for the validating
func (v *Validation) IsFail() bool {
return v.hasError
}
// IsSuccess for the validating
func (v *Validation) IsSuccess() bool {
return !v.hasError
}
// SafeData get all validated safe data
func (v *Validation) SafeData() M {
return v.safeData
}
// FilteredData return filtered data.
func (v *Validation) FilteredData() M {
return v.filteredData
}
/*************************************************************
* helper methods
*************************************************************/
func (v *Validation) shouldStop() bool {
return v.hasError && v.StopOnError
}
func (v *Validation) isNotNeedToCheck(field string) bool {
if len(v.sceneFields) == 0 {
return false
}
_, ok := v.sceneFields[field]
return !ok
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化