Giter Site home page Giter Site logo

gookit / validate Goto Github PK

View Code? Open in Web Editor NEW
1.0K 21.0 116.0 744 KB

⚔ Go package for data validation and filtering. support Map, Struct, Form data. Go通用的数据验证与过滤库,使用简单,内置大部分常用验证、过滤器,支持自定义验证器、自定义消息、字段翻译。

Home Page: https://gookit.github.io/validate/

License: MIT License

Go 99.75% HTML 0.25%
validation validator filter govalidator validate verification

validate's Introduction

Validate

GitHub tag (latest SemVer) GoDoc GitHub go.mod Go version Coverage Status Go Report Card Actions Status

validate is a generic Go data validate and filter tool library.

  • Support quick validate Map, Struct, Request(Form, JSON, url.Values, UploadedFile) data
    • Validating http.Request automatically collects data based on the request Content-Type value
    • Supports checking each child value in a slice. eg: v.StringRule("tags.*", "required|string")
  • Support filter/sanitize/convert data before validate
  • Support add custom filter/validator func
  • Support scene settings, verify different fields in different scenes
  • Support custom error messages, field translates.
    • Can use message, label tags in struct
  • Customizable i18n aware error messages, built in en, zh-CN, zh-TW
  • Built-in common data type filter/converter. see Built In Filters
  • Many commonly used validators have been built in(> 70), see Built In Validators
  • Can use validate in any frameworks, such as Gin, Echo, Chi and more
  • Supports direct use of rules to validate value. eg: validate.Val("[email protected]", "required|email")

中文说明请查看 README.zh-CN

Go Doc

Validate Struct

Use the validate tag of the structure, you can quickly config a structure.

Config the struct use tags

Field translations and error messages for structs can be quickly configured using the message and label tags.

  • Support configuration field mapping through structure tag, read the value of json tag by default
  • Support configuration error message via structure's message tag
  • Support configuration field translation via structure's label tag
package main

import (
	"fmt"
	"time"

	"github.com/gookit/validate"
)

// UserForm struct
type UserForm struct {
	Name     string    `validate:"required|min_len:7" message:"required:{field} is required" label:"User Name"`
	Email    string    `validate:"email" message:"email is invalid" label:"User Email"`
	Age      int       `validate:"required|int|min:1|max:99" message:"int:age must int|min:age min value is 1"`
	CreateAt int       `validate:"min:1"`
	Safe     int       `validate:"-"`
	UpdateAt time.Time `validate:"required" message:"update time is required"`
	Code     string    `validate:"customValidator"`
	// ExtInfo nested struct
	ExtInfo struct{
		Homepage string `validate:"required" label:"Home Page"`
		CityName string
	} `validate:"required" label:"Home Page"`
}

// CustomValidator custom validator in the source struct.
func (f UserForm) CustomValidator(val string) bool {
	return len(val) == 4
}

Config validate use struct methods

validate provides extended functionality:

The struct can implement three interfaces methods, which is convenient to do some customization:

  • ConfigValidation(v *Validation) will be called after the validator instance is created
  • Messages() map[string]string can customize the validator error message
  • Translates() map[string]string can customize field translation
package main

import (
	"fmt"
	"time"

	"github.com/gookit/validate"
)

// UserForm struct
type UserForm struct {
	Name     string    `validate:"required|min_len:7"`
	Email    string    `validate:"email"`
	Age      int       `validate:"required|int|min:1|max:99"`
	CreateAt int       `validate:"min:1"`
	Safe     int       `validate:"-"`
	UpdateAt time.Time `validate:"required"`
	Code     string    `validate:"customValidator"`
	// ExtInfo nested struct
	ExtInfo struct{
		Homepage string `validate:"required"`
		CityName string
	} `validate:"required"`
}

// CustomValidator custom validator in the source struct.
func (f UserForm) CustomValidator(val string) bool {
	return len(val) == 4
}

// ConfigValidation config the Validation
// eg:
// - define validate scenes
func (f UserForm) ConfigValidation(v *validate.Validation) {
	v.WithScenes(validate.SValues{
		"add":    []string{"ExtInfo.Homepage", "Name", "Code"},
		"update": []string{"ExtInfo.CityName", "Name"},
	})
}

// Messages you can custom validator error messages. 
func (f UserForm) Messages() map[string]string {
	return validate.MS{
		"required": "oh! the {field} is required",
		"email": "email is invalid",
		"Name.required": "message for special field",
		"Age.int": "age must int",
		"Age.min": "age min value is 1",
	}
}

// Translates you can custom field translates. 
func (f UserForm) Translates() map[string]string {
	return validate.MS{
		"Name": "User Name",
		"Email": "User Email",
		"ExtInfo.Homepage": "Home Page",
	}
}

Create and validating

Can use validate.Struct(ptr) quick create a validation instance. then call v.Validate() for validating.

package main

import (
  "fmt"

  "github.com/gookit/validate"
)

func main() {
	u := &UserForm{
		Name: "inhere",
	}
	
	v := validate.Struct(u)
	// v := validate.New(u)

	if v.Validate() { // validate ok
		// do something ...
	} else {
		fmt.Println(v.Errors) // all error messages
		fmt.Println(v.Errors.One()) // returns a random error message text
		fmt.Println(v.Errors.OneError()) // returns a random error
		fmt.Println(v.Errors.Field("Name")) // returns error messages of the field 
	}
}

Validate Map

You can also validate a MAP data directly.

package main

import (
"fmt"

"github.com/gookit/validate"
)

func main()  {
	m := map[string]any{
		"name":  "inhere",
		"age":   100,
		"oldSt": 1,
		"newSt": 2,
		"email": "[email protected]",
		"tags": []string{"go", "php", "java"},
	}

	v := validate.Map(m)
	// v := validate.New(m)
	v.AddRule("name", "required")
	v.AddRule("name", "minLen", 7)
	v.AddRule("age", "max", 99)
	v.AddRule("age", "min", 1)
	v.AddRule("email", "email")
	
	// can also
	v.StringRule("age", "required|int|min:1|max:99")
	v.StringRule("name", "required|minLen:7")
	v.StringRule("tags", "required|slice|minlen:1")
	// feat: support check sub-item in slice
	v.StringRule("tags.*", "required|string|min_len:7")

	// v.WithScenes(map[string]string{
	//	 "create": []string{"name", "email"},
	//	 "update": []string{"name"},
	// })
	
	if v.Validate() { // validate ok
		safeData := v.SafeData()
		// do something ...
	} else {
		fmt.Println(v.Errors) // all error messages
		fmt.Println(v.Errors.One()) // returns a random error message text
	}
}

Validate Request

If it is an HTTP request, you can quickly validate the data and pass the verification. Then bind the secure data to the structure.

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gookit/validate"
)

// UserForm struct
type UserForm struct {
	Name     string
	Email    string
	Age      int
	CreateAt int
	Safe     int
	UpdateAt time.Time
	Code     string
}

func main()  {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		data, err := validate.FromRequest(r)
		if err != nil {
			panic(err)
		}

		v := data.Create()
		// setting rules
		v.FilterRule("age", "int") // convert value to int
		
		v.AddRule("name", "required")
		v.AddRule("name", "minLen", 7)
		v.AddRule("age", "max", 99)
		v.StringRule("code", `required|regex:\d{4,6}`)

		if v.Validate() { // validate ok
			// safeData := v.SafeData()
			userForm := &UserForm{}
			v.BindSafeData(userForm)

			// do something ...
			fmt.Println(userForm.Name)
		} else {
			fmt.Println(v.Errors) // all error messages
			fmt.Println(v.Errors.One()) // returns a random error message text
		}
	})

	http.ListenAndServe(":8090", handler)
}

Quick Method

Quick create Validation instance.

  • New(data any, scene ...string) *Validation
  • Request(r *http.Request) *Validation
  • JSON(s string, scene ...string) *Validation
  • Struct(s any, scene ...string) *Validation
  • Map(m map[string]any, scene ...string) *Validation

Quick create DataFace instance.

  • FromMap(m map[string]any) *MapData
  • FromStruct(s any) (*StructData, error)
  • FromJSON(s string) (*MapData, error)
  • FromJSONBytes(bs []byte) (*MapData, error)
  • FromURLValues(values url.Values) *FormData
  • FromRequest(r *http.Request, maxMemoryLimit ...int64) (DataFace, error)

Create Validation from DataFace

d := FromMap(map[string]any{"key": "val"})
v := d.Validation()

Methods In Validation

  • func (v *Validation) Validate(scene ...string) bool Do validating and return is success.
  • func (v *Validation) ValidateE(scene ...string) Errors Do validating and return error.

More Usage

Validate Error

v.Errors is map data, top key is field name, value is map[string]string.

// do validating
if v.Validate() {
	return nil
}

// get errors
es := v.Errors

// check
es.Empty() // bool

// returns an random error, if no error returns nil
fmt.Println(v.Errors.OneError())
fmt.Println(v.Errors.ErrOrNil())

fmt.Println(v.Errors) // all error messages
fmt.Println(v.Errors.One()) // returns a random error message text
fmt.Println(v.Errors.Field("Name")) // returns error messages of the field 

Encode to JSON:

  • StopOnError=true(default), will only one error
{
    "field1": {
        "required": "error msg0"
    }
}
  • if StopOnError=false, will get multi error
{
    "field1": {
        "minLen": "error msg1",
        "required": "error msg0"
    },
    "field2": {
        "min": "error msg2"
    }
}

Global Option

You can adjust some processing logic of the validator by changing the global option settings.

// GlobalOption settings for validate
type GlobalOption struct {
	// FilterTag name in the struct tags.
	//
	// default: filter
	FilterTag string
	// ValidateTag in the struct tags.
	//
	// default: validate
	ValidateTag string
	// FieldTag the output field name in the struct tags.
	// it as placeholder on error message.
	//
	// default: json
	FieldTag string
	// LabelTag the display name in the struct tags.
	// use for define field translate name on error.
	//
	// default: label
	LabelTag string
	// MessageTag define error message for the field.
	//
	// default: message
	MessageTag 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
	// CheckZero Whether validate the default zero value. (intX,uintX: 0, string: "")
	CheckZero bool
	// CheckSubOnParentMarked True: only collect sub-struct rule on current field has rule.
	CheckSubOnParentMarked bool
	// ValidatePrivateFields Whether to validate private fields or not, especially when inheriting other other structs.
	//
	//  type foo struct {
	//	  Field int `json:"field" validate:"required"`
	//  }
	//  type bar struct {
	//    foo // <-- validate this field
	//    Field2 int `json:"field2" validate:"required"`
	//  }
	//
	// default: false
	ValidatePrivateFields bool
}

Usage:

// change global opts
validate.Config(func(opt *validate.GlobalOption) {
	opt.StopOnError = false
	opt.SkipOnEmpty = false
})

Validating Private (Unexported fields)

By default, private fields are skipped. It is not uncommon to find code such as the following

type foo struct {
	somefield int
}

type Bar struct {
	foo
	SomeOtherField string
}

In order to have foo.somefield validated, enable the behavior by setting GlobalOption.ValidatePrivateFields to true.

validate.Config(func(opt *validate.GlobalOption) {
	opt.ValidatePrivateFields = true
})

Custom Error Messages

  • Register language messages
import "github.com/gookit/validate/locales/zhcn"

// for all Validation.
// NOTICE: must be registered before on validate.New(), it only need call at once.
zhcn.RegisterGlobal()

// ... ...

v := validate.New()

// only for current Validation
zhcn.Register(v)
  • Manual add global messages
validate.AddGlobalMessages(map[string]string{
    "minLength": "OO! {field} min length is %d",
})
  • Add messages for current validation
v := validate.New(map[string]any{
    "name": "inhere",
})
v.StringRule("name", "required|string|minLen:7|maxLen:15")

v.AddMessages(map[string]string{
    "minLength": "OO! {field} min length is %d",
    "name.minLen": "OO! username min length is %d",
})
  • Use struct tags: message, label
type UserForm struct {
    Name  string `validate:"required|minLen:7" label:"User Name"`
    Email string `validate:"email" message:"email is invalid" label:"User Email"`
}
  • Use struct method Messages()
// Messages you can custom validator error messages. 
func (f UserForm) Messages() map[string]string {
	return validate.MS{
		"required": "oh! the {field} is required",
		"Name.required": "message for special field",
	}
}

Add Custom Validator

validate supports adding custom validators, and supports adding global validator and temporary validator.

  • Global Validator is globally valid and can be used everywhere
  • Temporary Validator added to the current validation instance, only the current validation is available
  • Add verification method to the structure. How to use please see the structure verification example above

Note: The validator method must return a bool to indicate whether the validation was successful. The first parameter is the corresponding field value. If there are additional parameters, they will be appended automatically.

Add Global Validator

You can add one or more custom validators at once.

validate.AddValidator("myCheck0", func(val any) bool {
	// do validate val ...
	return true
})
validate.AddValidators(validate.M{
	"myCheck1": func(val any) bool {
		// do validate val ...
		return true
	},
})

Add Temporary Validator

Again, you can add one or more custom validators at once.

v := validate.Struct(u)
v.AddValidator("myFunc3", func(val any) bool {
	// do validate val ...
	return true
})
v.AddValidators(validate.M{
	"myFunc4": func(val any) bool {
		// do validate val ...
		return true
	},
})

Add Custom Filter

validate can also support adding custom filters, and supports adding global filter and temporary filter.

  • Global Filter is globally valid and can be used everywhere
  • Temporary Filter added to the current validation instance, only the current validation is available

TIP: for filter func, we allow functions with 1 result or 2 results where the second is an error.

Add Global Filter

You can add one or more custom validators at once.

package main

import "github.com/gookit/validate"

func init() {
	validate.AddFilter("myToIntFilter0", func(val any) int {
		// do filtering val ...
		return 1
	})
	validate.AddFilters(validate.M{
		"myToIntFilter1": func(val any) (int, error) {
			// do filtering val ...
			return 1, nil
		},
	})
}

Add Temporary Filter

Again, you can add one or more custom filters at once.

package main

import "github.com/gookit/validate"

func main() {
	v := validate.New(&someStrcut{})

	v.AddFilter("myToIntFilter0", func(val any) int {
		// do filtering val ...
		return 1
	})
	v.AddFilters(validate.M{
		"myToIntFilter1": func(val any) (int, error) {
			// do filtering val ...
			return 1, nil
		},
	})
	// use the added filter
	v.FilterRule("field", "myToIntFilter0")
}

Custom required validation

Allows a custom required validator to customize whether the validation is empty. However, note that the validator name must start with required, e.g. required_custom.

	type Data struct {
		Age  int    `validate:"required_custom" message:"age is required"`
		Name string `validate:"required"`
	}

	v := validate.New(&Data{
		Name: "tom",
		Age:  0,
	})

	v.AddValidator("required_custom", func(val any) bool {
		// do check value
		return false
	})

	ok := v.Validate()
	assert.False(t, ok)

Use on gin framework

Can use validate in any frameworks, such as Gin, Echo, Chi and more.

Examples on gin:

package main
import (
    "github.com/gin-gonic/gin/binding"
    "github.com/gookit/validate"
)

// implements the binding.StructValidator
type customValidator struct {}

func (c *customValidator) ValidateStruct(ptr any) error {
    v := validate.Struct(ptr)
    v.Validate() // do validating
    
    if v.Errors.Empty() {
	return nil
    }

    return v.Errors
}

func (c *customValidator) Engine() any {
    return nil
}

func main()  {
	// ...

    // after init gin, set custom validator
    binding.Validator = &customValidator{}
}

Built In Validators

Camel-style validator names now have underlined aliases. endsWith can also be written as ends_with

validator/aliases description
required Check value is required and cannot be empty.
required_if/requiredIf required_if:anotherfield,value,... The field under validation must be present and not empty if the anotherField field is equal to any value.
requiredUnless required_unless:anotherfield,value,... The field under validation must be present and not empty unless the anotherField field is equal to any value.
requiredWith required_with:foo,bar,... The field under validation must be present and not empty only if any of the other specified fields are present.
requiredWithAll required_with_all:foo,bar,... The field under validation must be present and not empty only if all of the other specified fields are present.
requiredWithout required_without:foo,bar,... The field under validation must be present and not empty only when any of the other specified fields are not present.
requiredWithoutAll required_without_all:foo,bar,... The field under validation must be present and not empty only when all of the other specified fields are not present.
-/safe The field values are safe and do not require validation
int/integer/isInt Check value is intX uintX type, And support size checking. eg: "int" "int:2" "int:2,12"
uint/isUint Check value is uint(uintX) type, value >= 0
bool/isBool Check value is bool string(true: "1", "on", "yes", "true", false: "0", "off", "no", "false").
string/isString Check value is string type.
float/isFloat Check value is float(floatX) type
slice/isSlice Check value is slice type([]intX []uintX []byte []string ...).
in/enum Check if the value is in the given enumeration "in:a,b"
not_in/notIn Check if the value is not in the given enumeration "contains:b"
contains Check if the input value contains the given value
not_contains/notContains Check if the input value not contains the given value
string_contains/stringContains Check if the input string value is contains the given sub-string
starts_with/startsWith Check if the input string value is starts with the given sub-string
ends_with/endsWith Check if the input string value is ends with the given sub-string
range/between Check that the value is a number and is within the given range
max/lte Check value is less than or equal to the given value
min/gte Check value is greater than or equal to the given value(for intX uintX floatX)
eq/equal/isEqual Check that the input value is equal to the given value
ne/notEq/notEqual Check that the input value is not equal to the given value
lt/lessThan Check value is less than the given value(use for intX uintX floatX)
gt/greaterThan Check value is greater than the given value(use for intX uintX floatX)
email/isEmail Check value is email address string.
intEq/intEqual Check value is int and equals to the given value.
len/length Check value length is equals to the given size(use for string array slice map).
regex/regexp Check if the value can pass the regular verification
arr/list/array/isArray Check value is array, slice type
map/isMap Check value is a MAP type
strings/isStrings Check value is string slice type(only allow []string).
ints/isInts Check value is int slice type(only allow []int).
min_len/minLen/minLength Check the minimum length of the value is the given size
max_len/maxLen/maxLength Check the maximum length of the value is the given size
eq_field/eqField Check that the field value is equals to the value of another field
ne_field/neField Check that the field value is not equals to the value of another field
gte_field/gteField Check that the field value is greater than or equal to the value of another field
gt_field/gtField Check that the field value is greater than the value of another field
lte_field/lteField Check if the field value is less than or equal to the value of another field
lt_field/ltField Check that the field value is less than the value of another field
file/isFile Verify if it is an uploaded file
image/isImage Check if it is an uploaded image file and support suffix check
mime/mimeType/inMimeTypes Check that it is an uploaded file and is in the specified MIME type
date/isDate Check the field value is date string. eg 2018-10-25
gt_date/gtDate/afterDate Check that the input value is greater than the given date string.
lt_date/ltDate/beforeDate Check that the input value is less than the given date string
gte_date/gteDate/afterOrEqualDate Check that the input value is greater than or equal to the given date string.
lte_date/lteDate/beforeOrEqualDate Check that the input value is less than or equal to the given date string.
has_whitespace/hasWhitespace Check value string has Whitespace.
ascii/ASCII/isASCII Check value is ASCII string.
alpha/isAlpha Verify that the value contains only alphabetic characters
alphaNum/isAlphaNum Check that only letters, numbers are included
alphaDash/isAlphaDash Check to include only letters, numbers, dashes ( - ), and underscores ( _ )
multiByte/isMultiByte Check value is MultiByte string.
base64/isBase64 Check value is Base64 string.
dns_name/dnsName/DNSName/isDNSName Check value is DNSName string.
data_uri/dataURI/isDataURI Check value is DataURI string.
empty/isEmpty Check value is Empty string.
hex_color/hexColor/isHexColor Check value is Hex color string.
hexadecimal/isHexadecimal Check value is Hexadecimal string.
json/JSON/isJSON Check value is JSON string.
lat/latitude/isLatitude Check value is Latitude string.
lon/longitude/isLongitude Check value is Longitude string.
mac/isMAC Check value is MAC string.
num/number/isNumber Check value is number string. >= 0
cn_mobile/cnMobile/isCnMobile Check value is china mobile number string.
printableASCII/isPrintableASCII Check value is PrintableASCII string.
rgb_color/rgbColor/RGBColor/isRGBColor Check value is RGB color string.
url/isURL Check value is URL string.
fullUrl/isFullURL Check value is full URL string(must start with http,https).
ip/isIP Check value is IP(v4 or v6) string.
ipv4/isIPv4 Check value is IPv4 string.
ipv6/isIPv6 Check value is IPv6 string.
CIDR/isCIDR Check value is CIDR string.
CIDRv4/isCIDRv4 Check value is CIDRv4 string.
CIDRv6/isCIDRv6 Check value is CIDRv6 string.
uuid/isUUID Check value is UUID string.
uuid3/isUUID3 Check value is UUID3 string.
uuid4/isUUID4 Check value is UUID4 string.
uuid5/isUUID5 Check value is UUID5 string.
filePath/isFilePath Check value is an existing file path
unixPath/isUnixPath Check value is Unix Path string.
winPath/isWinPath Check value is Windows Path string.
isbn10/ISBN10/isISBN10 Check value is ISBN10 string.
isbn13/ISBN13/isISBN13 Check value is ISBN13 string.

Notice:

  • intX is contains: int, int8, int16, int32, int64
  • uintX is contains: uint, uint8, uint16, uint32, uint64
  • floatX is contains: float32, float64

Built In Filters

Filters powered by: gookit/filter

filter/aliases description
int/toInt Convert value(string/intX/floatX) to int type v.FilterRule("id", "int")
uint/toUint Convert value(string/intX/floatX) to uint type v.FilterRule("id", "uint")
int64/toInt64 Convert value(string/intX/floatX) to int64 type v.FilterRule("id", "int64")
float/toFloat Convert value(string/intX/floatX) to float type
bool/toBool Convert string value to bool. (true: "1", "on", "yes", "true", false: "0", "off", "no", "false")
trim/trimSpace Clean up whitespace characters on both sides of the string
ltrim/trimLeft Clean up whitespace characters on left sides of the string
rtrim/trimRight Clean up whitespace characters on right sides of the string
int/integer Convert value(string/intX/floatX) to int type v.FilterRule("id", "int")
lower/lowercase Convert string to lowercase
upper/uppercase Convert string to uppercase
lcFirst/lowerFirst Convert the first character of a string to lowercase
ucFirst/upperFirst Convert the first character of a string to uppercase
ucWord/upperWord Convert the first character of each word to uppercase
camel/camelCase Convert string to camel naming style
snake/snakeCase Convert string to snake naming style
escapeJs/escapeJS Escape JS string.
escapeHtml/escapeHTML Escape HTML string.
str2ints/strToInts Convert string to int slice []int
str2time/strToTime Convert date string to time.Time.
str2arr/str2array/strToArray Convert string to string slice []string

Gookit packages

  • gookit/ini Go config management, use INI files
  • gookit/rux Simple and fast request router for golang HTTP
  • gookit/gcli build CLI application, tool library, running CLI commands
  • gookit/event Lightweight event manager and dispatcher implements by Go
  • gookit/cache Generic cache use and cache manager for golang. support File, Memory, Redis, Memcached.
  • gookit/config Go config management. support JSON, YAML, TOML, INI, HCL, ENV and Flags
  • gookit/color A command-line color library with true color support, universal API methods and Windows support
  • gookit/filter Provide filtering, sanitizing, and conversion of golang data
  • gookit/validate Use for data validation and filtering. support Map, Struct, Form data
  • gookit/goutil Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more
  • More please see https://github.com/gookit

See also

License

MIT

validate's People

Contributors

alexbrin avatar allenx2018 avatar caoyong2619 avatar cpj555 avatar d7561985 avatar dependabot-preview[bot] avatar dependabot[bot] avatar eloycoto avatar fishyww avatar herosizy avatar inhere avatar jeanslin avatar junaid-ali avatar kaptinlin avatar mariogao avatar mlukasik-dev avatar oscarvanl avatar quetzyg avatar sharkfoursix avatar sujit-baniya avatar zenpk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

validate's Issues

自定义规则时如何获取规则名后的参数

type Request struct{
      Name string `validate:"exists:user:name"`
}


v.AddValidator("exists", func(val interface{}) bool {
		return false
	})

v.AddValidator("exists", func(val interface{},args []interface) bool {
		return false
	})

上面两种方式,第一种获取不到参数,第二种提示只不是个slice。自定义时如何获取规则后的参数呢?

内置过滤器字段是驼峰无效

如果用struct方式使用,且字段名字是大驼峰,过滤器无效。

type SmsReq struct {
	CountryCode string `json:"country_code" validate:"required" filter:"trim|lower"`
	Phone       string `json:"phone" validate:"required" filter:"trim"`
	Type        string `json:"type" validate:"required|in:register,forget_password,set_pay_password,reset_pay_password,reset_password" filter:"trim"`
}
CountryCode 不会过滤但是改为一个单词的时候就会

版本
1.1.1

有些验证字段不生效

使用方法

type SetProfileReq struct {
	Nickname string `json:"nickname" validate:"" filter:"trim"`
	Avatar   string `json:"avatar" validate:"required|url" filter:"trim"`
}

func checkSetProfileReq(ctx echo.Context, req *SetProfileReq) (*response.NO, error) {
	if err := ctx.Bind(req); err != nil {
		return response.NewNO(
			response.InvalidArgument,
			locale.Message(ctx, "invalid_argument"),
		), errors.Wrapf(err, "bind param err")
	}

	fmt.Println("bind:", req)

	if err := ctx.Validate(req); err != nil {
		return response.NewNO(
			response.InvalidArgument,
			locale.Message(ctx, "invalid_argument"),
		), errors.Wrapf(err, "validate param err")
	}

	fmt.Println("validated:", req)

	return nil, nil
}

// validate方法的实现
type Validate struct {
}

func (Validate) Validate(i interface{}) error {
	v := validate.Struct(i)
	if !v.Validate() {
		return v.Errors
	}
	return v.BindSafeData(i)
}

Avatar使用了两个规则,required和url。但是url规则不生效。
测试的结果

bind: &{123nickname111 1}
validated: &{123nickname111 1}

验证通过,没有校验url格式。
我换另一个规则,如required|ip,这样也是生效的。会检查ip格式。

版本信息

1.1.3

能否请在文档中确认用法:可以为空,但有值必须满足条件。

有的时候,一个值是可以为空的,代表不更新。但是一旦有值,则希望这个值是满足后续条件的。能否请详细指教这样的场景用法?(比如,现在版本不加required,就是满足这种需求的,但是以文本的方式在README里面加以确认,应该可以解除部分像我这样的困惑吧。)

谢谢。

结构体定义错误信息后续的错误信息无法使用message tag

type RegisterRequest struct {
	Username string `json:"username" validate:"required" message:"请输入正确的用户名"`
	Password string `json:"password" validate:"required" message:"登录密码不能为空"`
	Confirm  string `json:"confirm"  validate:"required|eqField:password" message:"重复密码必须与登录密码一致"`
	Phone    string `json:"phone"    validate:"required|isCnMobile" message:"请填写正确格式的手机号码"`
	Code     string `json:"code"     validate:"required" message:"请填写正确格式的手机验证码"`
}

在Confirm定义了两个验证规则:required与eqField。
实际输出是:confirm value must be equal the field password

regexp 无法匹配

类似这样的正则应该如何在 struct 中书写规则?

^(SLOTS|LIVE|SPORTS|LOTTERY)(,(SLOTS|LIVE|SPORTS|LOTTERY))*$

参数会用 | 分隔,跟踪得到匹配规则变为 ^(SLOTS

如果改为
^(SLOTS\|LIVE\|SPORTS\|LOTTERY)(,(SLOTS\|LIVE\|SPORTS\|LOTTERY))*$
则该条规则丢失

plus: 正则表达式如有 , 也会导致规则丢失

json属性的值为数值类型,panic了

规则:
"cost_type": "required|number|min:1",

json数据

{
   "cost_type": 10 // 修改为 "10" 则不会panic
}
runtime/debug.Stack(0x2016ffd, 0x2, 0xc0000195c8)
	/usr/local/Cellar/go/1.13.4/libexec/src/runtime/debug/stack.go:24 +0x9d
git.liebaopay.com/INA_financial/rpc_services/app/srv/workflow/services.(*Workflow).CreateTask.func1(0xc00042e780)
	/app/go/rpc_services/app/srv/workflow/services/task.go:46 +0xde
panic(0x1e59a20, 0xc0002c2210)
	/usr/local/Cellar/go/1.13.4/libexec/src/runtime/panic.go:679 +0x1b2
github.com/gookit/validate.callValidator(0xc000470370, 0xc000367300, 0xc0004483d0, 0x9, 0x1dbc500, 0xc0004480f0, 0x0, 0x0, 0x0, 0x1)
	/Users/chenjiang/go/pkg/mod/github.com/gookit/[email protected]/validate.go:228 +0x11d3
github.com/gookit/validate.(*Rule).valueValidate(0xc000157f40, 0xc0004483d0, 0x9, 0x201e3b2, 0x8, 0x2016f01, 0x1dbc500, 0xc0004480f0, 0xc000470370, 0xc0002fa801)
	/Users/chenjiang/go/pkg/mod/github.com/gookit/[email protected]/validate.go:186 +0x17c
github.com/gookit/validate.(*Rule).Apply(0xc000157f40, 0xc000470370, 0x0)
	/Users/chenjiang/go/pkg/mod/github.com/gookit/[email protected]/validate.go:83 +0x6ba
github.com/gookit/validate.(*Validation).Validate(0xc000470370, 0x0, 0x0, 0x0, 0xc000470370)
	/Users/chenjiang/go/pkg/mod/github.com/gookit/[email protected]/validation.go:332 +0x109
git.liebaopay.com/INA_financial/rpc_services/app/srv/workflow/util/rule.Check(0xc000154000, 0x2f9, 0x300, 0x1, 0x300, 0x0)
	/app/go/rpc_services/app/srv/workflow/util/rule/rulevalidate.go:43 +0x3d6
git.liebaopay.com/INA_financial/rpc_services/app/srv/workflow/services.(*Workflow).CreateTask(0xc000019ed8, 0x22155e0, 0xc0000ba008, 0xc0000e0910, 0xc00042e780, 0x0, 0x0)
	/app/go/rpc_services/app/srv/workflow/services/task.go:109 +0xd2e
git.liebaopay.com/INA_financial/rpc_services/app/srv/workflow/services.TestAddTask(0xc000131500)
	/app/go/rpc_services/app/srv/workflow/services/task_test.go:109 +0x1b3
testing.tRunner(0xc000131500, 0x2073fd8)
	/usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:909 +0xc9
created by testing.(*T).Run
	/usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:960 +0x350
	{"srv_name": "workflow_run"}
code:fail message:"interface conversion: interface {} is float64, not string" 
--- PASS: TestAddTask (0.73s)

number 只能判断 字符串中的数值,希望可以增强一下
或者添加一个numeric表示数值类型,无论是字符串数值还是数值类
但不应该panic ?

int类型验证

package main

import "fmt"

import "github.com/gookit/validate"

func main() {
	m := map[string]interface{}{
		"name":  "inhere1",
		"age":   12.1234,
	}

	v := validate.Map(m)
        v.AddRule("age", "int")
        if v.Validate() { // validate ok
		fmt.Println(m)
	} else {
		fmt.Println(v.Errors)       // all error messages
		fmt.Println(v.Errors.One()) // returns a random error message text
	}
}

为什么上面的这个可以验证通过呀

feat: 支持字段默认值

	type User struct {
		Name string `validate:"required|default:tom" filter:"trim|upper"`
		Age int `validate:"uint|default:23"`
	}

uint 为 0 时无法验证最小值

结构体如下:

type Tag struct {
	ID   uint   `validate:"min:2"`
	Name string `validate:"required|maxLen:20"`
}

Tag.ID = 0 验证通过,有问题

Tag.ID = 1 验证不通过,这是对的

@inhere

validate.Errors希望增加一个trans功能

在集成echo的发现的问题,最后单独实现了一个validate接口给echo,内部调用struct,最终给前端的时候,是error接口,强行转回validate.Errors类型,但是这个时候已经没办法重新格式化语种

Some issue

  • If FromRequest func is called, the request body is nil.

  • Instance Validation struct every time?

type p struct  {
    Name string "validate:required"
}
type p2 struct  {
    Name2 string "validate:required"
}
p1 := p{Name: "name1"}
p2 := p{Name2: "name2"}
validate.Struct(p1)
validate.Struct(p2)

validate.Struct every time return new Validation.
Can instance once Validation?

float64类型验证错误

float64类型验证报错

package main

import (
	"github.com/gookit/validate"
	"fmt"
)
type Fl struct {
	A float64 `validate:"float"`
}
func main() {
	fl := Fl{123}
	va := validate.Struct(fl)
	fmt.Println(va.Validate())
}
panic: reflect: Call using float64 as type string

goroutine 1 [running]:
reflect.Value.call(0x6d76e0, 0x74fc60, 0x13, 0x7392dd, 0x4, 0xc0420f1460, 0x1, 0x1, 0x738360, 0x6c71e0, ...)
	C:/setup/go/src/reflect/value.go:377 +0x1209
reflect.Value.Call(0x6d76e0, 0x74fc60, 0x13, 0xc0420f1460, 0x1, 0x1, 0x779b00, 0x6d76e0, 0xc042183cd8)
	C:/setup/go/src/reflect/value.go:308 +0xab
github.com/gookit/validate.callValidatorValue(0x6d76e0, 0x74fc60, 0x13, 0x6c71e0, 0xc042132830, 0x0, 0x0, 0x0, 0x6c71e0)
	E:/crm/src/github.com/gookit/validate/validate.go:467 +0x1c6
github.com/gookit/validate.callValidator(0xc0420be2c0, 0xc042066f80, 0x6ab245, 0x1, 0x6c71e0, 0xc042132830, 0x0, 0x0, 0x0, 0x68c7de)
	E:/crm/src/github.com/gookit/validate/validate.go:449 +0x214
github.com/gookit/validate.(*Rule).valueValidate(0xc042135400, 0x6ab245, 0x1, 0x739e1b, 0x7, 0x6c71e0, 0xc042132830, 0xc0420be2c0, 0x0)
	E:/crm/src/github.com/gookit/validate/validate.go:295 +0x184
github.com/gookit/validate.(*Rule).Apply(0xc042135400, 0xc0420be2c0, 0x0)
	E:/crm/src/github.com/gookit/validate/validate.go:188 +0x603
github.com/gookit/validate.(*Validation).Validate(0xc0420be2c0, 0x0, 0x0, 0x0, 0x0)
	E:/crm/src/github.com/gookit/validate/validation.go:399 +0x11e

验证图片失败!Failed to validate the image

我上传一个照片 / i upload a photo.

当我在BEEGO中获取文件 / when i get it in beego
f, _ , _ := this.GetFile(k)

并且创建一个validate规则 / and i create a validate rule
v.AddRule("thumbnail", "image")

控制台提示debug信息:thumbnail must be an uploaded image file
The console debug information: thumbnail must be an uploaded image file

bug: 场景验证

这个是正常的预期,名字不符合长度

image

如果是这样,带上场景,就都不验证了

image

版本信息

validate的版本是1.1.1
go version go1.12.6 darwin/amd64

自定义错误内容不生效

示例代码:

	m := map[string]interface{}{
		"title": "1",
	}
	v := validate.Map(m)
	v.StringRule("title", "in:2,3")
	v.AddMessages(map[string]string{
		"in":"自定义错误",
	})
	if !v.Validate() {
		fmt.Println(v.Errors.One())
	}

输出(没有输出自定义错误):

title value must be in the enum [2 3]

如何在Create和Update**用一套 验证规则

大佬又是我。。这次是在开发中遇到的问题,我现在是用验证规则绑定在了结构体的tag
这样创建是没有什么问题,但是在更新数据的时候会有问题,前端传递过来的更新数据 有些项不需要更新的话传的是空,还有些自定义的验证器是创建用的和更新的时候冲突的,这样一来数据的更新就没办法来验证。求教大佬有什么好的方式么?

添加regex时报错,提示输入参数过多

v.StringRule("code", required|regex:\d{4,6})

panic: validate: the number of parameters given does not match the required. validator 'regex', want 2, given 3

goroutine 1 [running]:
validate.panicf(...)
	validate/helper.go:287
validate.(*funcMeta).checkArgNum(0xc0000c7dc0, 0x3, 0x13ab011, 0x5)
	validate/validators.go:198 +0x1b6
validate.(*Rule).valueValidate(0xc0001ee000, 0x13a874a, 0x4, 0x13a8e15, 0x6, 0x13a8301, 0x1331a60, 0xc0000bae30, 0xc0000f22c0, 0x0)
	validate/validate.go:166 +0x1c7
validate.(*Rule).Apply(0xc0001ee000, 0xc0000f22c0, 0x0)
	validate/validate.go:83 +0x6c0
validate.(*Validation).Validate(0xc0000f22c0, 0x0, 0x0, 0x0, 0xd)
	validate/validation.go:332 +0x10c

exit status 2

TODO 请问有方法实现结构体的嵌套嘛

在使用protobuf的时候,起先定义了一个User,在User里面定义了字段和校验,然后在请求的message中直接message UserNewReq {User user = 1;},这时候好像没有去校验字段。

怎么设置正则表达式验证

作者您好
看代码是有正则表达式验证的regexp
但是具体怎么使用可以给一个例子吗?
感激不尽

例如 Age字段只能为1-3位数字 \d{1-3}
在结构体里定义验证规则的时候,应该怎么定义呢?

required好像不支持指针变量的校验.

我使用go-playground校验器的时候, 校验零值只需要定义指针就行了, 但是这里好像不行. 下面是我的demo例子.

package test

import (
	"fmt"
	"github.com/gookit/validate"
	_ "novel/bootstrap"
	"testing"
)


// UserForm struct
type UserDto struct {
	// Name     string    `validate:"required|minLen:7"`
	Name     string    `validate:"required"`
	Sex   	*bool	   `validate:"required"`
}


// Messages 您可以自定义验证器错误消息
func (ud UserDto) Messages() map[string]string {
	return validate.MS{
		"required": "oh! the {field} is required",
		"Name.required": "name 必填",
	}
}

func testValidate(ud *UserDto)  {
	// 创建 Validation 实例
	v := validate.Struct(ud)
	if !v.Validate() {
		fmt.Println(v.Errors)
	} else {
		fmt.Println("Success...")
	}
}

func TestMyValidator(t *testing.T)  {
	sex := true
	u := UserDto{
		// Name: "inhere",
		Name: "abc",
		Sex: &sex,
	}
	testValidate(&u)
	// 下面如果Sex不传或传nil, 会panic错误
	testValidate(&UserDto{
		Name: "abc",
		Sex: nil,
	})
}

请问该如何解决零值的校验问题呢?

正则表达不支持使用“|”语法吗?


import (
	"fmt"
	"github.com/gookit/validate"
)

func main() {
	m := map[string]interface{}{
		"name": "PCAP",
	}
	v := validate.Map(m)
	v.StringRule("name", "regex:^(PCAP|DOC)$")
	if v.Validate() { // validate ok
		fmt.Println("validate success")
	} else {
		fmt.Println(v.Errors)       // all error messages
		fmt.Println(v.Errors.One()) // returns a random error message text
	}
}

当值为int 0时required会提示验证错误

type AddMenuRequest struct {
	ParentId  int64  `json:"parent_id"  validate:"required|uint" message:"required:父级ID不能为空|uint:父级ID参数异常"`
}

当字段必填并且可以为0时就不能这么使用验证了,它会告诉我这个字段的值为空。
我只能验证uint,再在别的地方另外去判断到底有没有传parent_id这个字段

希望增加多级校验规则

多级验证规则示例:

{
      "data": [
            {
                  name: xx,
                  id: xx,
                  rate: xx, 
             },
            {
                  name: xx,
                  id: xx,
                  rate: xx, 
             },
       ]
}

规则:
"data.*.name" =>"required|string|minLen:5",
"data.*.id" =>"required|int|min:1",
"data.*.rate" =>"required|float|count:100",   //合计为100, minCount:99最少99,maxCount:100.1最大100.1
required_unless:anotherfield,value,...
如果其它字段 _anotherfield_ 不等于任一值 _value_ ,则此验证字段必须存在且不为空。


required_with:foo,bar,...
在其他任一指定字段出现时,验证的字段才必须存在且不为空。


required_with_all:foo,bar,...
只有在其他指定字段全部出现时,验证的字段才必须存在且不为空。


required_without:foo,bar,...
在其他指定任一字段不出现时,验证的字段才必须存在且不为空。


required_without_all:foo,bar,...
只有在其他指定字段全部不出现时,验证的字段才必须存在且不为空。

starts_with:foo,bar,...
验证字段必须以给定值之一开头。

更多可参考规则验证器
https://learnku.com/docs/laravel/6.x/validation/5144#c58a91

data_source.go 第 327 行的 Set() 方法

Set(field string, val interface{})

参数分别是 “Msg2”(string),“ping”(string)

在 355 行,newVal, err = convertType(val, srcKind, fv.Kind())
返回的 newVal 和 err 均为 nil

因为 err 为 nil
接下来的

if err != nil {
	return nil, err
}

没有生效

导致 361 行的fv.Set(reflect.ValueOf(newVal))接收的参数 newVal 以 nil 传入

这里直接 panic,而且也没有错误处理

请问,Set()方法有什么作用?为了解决刚才我遇到的这个问题,我让 set()方法直接返回了 参数中传入的 val

How to apply filter via struct?

https://github.com/gookit/validate/blob/master/validation_test.go#L126 - this is a nice way to apply validators into specific struct property. Is there a way to apply filters at the same line?

I've tried:

type Whatever struct {
	Uno    string `validate:"required|lower|in:one,two,true" `             // --> validate: the validator 'lower' is not exists
	Dos    string `validate:"required|upper|enum:FOUR,FIVE,SIX" `          // --> validate: the validator 'upper' is not exists
	Tres   string `filter:"upper" validate:"required|in:ONE,TWO,THREE" `   // passing 'one' --> Tres value must be in the enum [ONE TWO THREE]
	Cuatro string `filter:"lower" validate:"required|enum:four,five,six" ` // passing 'SiX' --> Cuatro value must be in the enum [four five six]
}

but without success. Adding validation.FilterRule("Tres", "trim|upper") works, but it is somehow distant from those validation rules (within a struct). I would like to keep those together. Can you update the Readme. Or at least provide some examples in here?

如果struct里面嵌套一个struct就验证不了了,请问有办法吗

如果struct里面嵌套一个struct就验证不了了,请问有办法吗

type AdminUserLoginRequest struct {
Localtime uint64 json:"localtime" binding:"required"
Data struct {
UserName string json:"username" validate:"required|string|CheckExists"
Password string json:"password" validate:"required|CheckPasswordIsEq"
} json:"data" binding:"required"
}

ptr type field not work as expected

// Test_validate_with_point 
// failed
//  Note: ptr field name expected works, but not actually
func Test_validate_with_point(t *testing.T) {
	type Foo struct {
		Name *string `validate:"in:henry,jim"`
	}
	name := "henry"
	valid := validate.New(&Foo{Name: &name})
	if !valid.Validate() {
		t.Log("err:", valid.Errors)
	}
}

// Test_validate_without_point 
// this tests will pass
func Test_validate_without_point(t *testing.T) {
	type Foo struct {
		Name string `validate:"in:henry,jim"`
	}
	name := "henry"
	valid := validate.New(&Foo{Name: name})
	if !valid.Validate() {
		t.Log("err:", valid.Errors)
	}
}

validate: custom type check

type STATUS int32
var s1 STATUS = 1
func TestValidation_Enum(t *testing.T) {
	v := New(M{
		"age":     s1,
	})
	v.StringRules(MS{
		"age": "required|in:1,2,3,4",
	})

	v.Validate()

	fmt.Println(v.Errors)
}
age:
 age value must be in the enum [1 2 3 4]

能否有更明确的文档描述 contains/in 这些的用法?

相对而言,=, >, <, =>, <=, 这些应该都比较直观。isXXX,required,这些也都很简洁直观。

但有些就不那么直观,比如:

validate="contains=[1,2,‘3’]"
validate="contains=1,2,3|..."
validate="in=(1,2,3)"
validate="range =1-2"
validate="range =3,4"
validate="range =(5,6)"
validate="range =[7,8]"

似乎都有可能。很多细节都有赖于开发者最初决定选用的方式。
但实际上合理的方式通常只有一种。

如果文档里可以直接写清楚范例,比大量的文字描述要更加直观清楚很多。


我斗胆猜一下,等下去试试……
validate="in:0,1,2"
看看会不会报错(捂脸

奇怪的返回值,年龄最少18岁%!(EXTRA int64=18)

提交的数据是
age=10
www.xxx.com/test?age=10

结构体里定义的是
Age int form:"age" json:"age" validate:"required|int|min:18|max:150"``

自定义返回的信息是

`return validate.MS{

	"Age.min":"年龄最少18岁",
	"Age.max":"年龄最大150岁",
}

`

实际上返回的错误信息是
年龄最少18岁%!(EXTRA int64=18)
不知道为什么突然会冒出来 %!(EXTRA int64=18)
为什么会突然冒出来这样的?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.