用户登录和接口鉴权

This commit is contained in:
2025-09-07 21:13:15 +08:00
parent c4522b974b
commit 565cf3fa6a
380 changed files with 18330 additions and 16854 deletions

View File

@@ -3,7 +3,7 @@ GOCMD=go
linters-install:
@golangci-lint --version >/dev/null 2>&1 || { \
echo "installing linting tools..."; \
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v2.0.2; \
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.41.1; \
}
lint: linters-install

View File

@@ -1,7 +1,8 @@
Package validator
=================
<img align="right" src="logo.png">[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/go-playground/validator)](https://github.com/go-playground/validator/releases)
[![Build Status](https://github.com/go-playground/validator/actions/workflows/workflow.yml/badge.svg)](https://github.com/go-playground/validator/actions)
<img align="right" src="logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![Project status](https://img.shields.io/badge/version-10.20.0-green.svg)
[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
[![GoDoc](https://godoc.org/github.com/go-playground/validator?status.svg)](https://pkg.go.dev/github.com/go-playground/validator/v10)
@@ -21,11 +22,6 @@ It has the following **unique** features:
- Customizable i18n aware error messages.
- Default validator for the [gin](https://github.com/gin-gonic/gin) web framework; upgrading from v8 to v9 in gin see [here](https://github.com/go-playground/validator/tree/master/_examples/gin-upgrading-overriding)
A Call for Maintainers
----------------------
Please read the discussiong started [here](https://github.com/go-playground/validator/discussions/1330) if you are interested in contributing/helping maintain this package.
Installation
------------
@@ -167,12 +163,10 @@ validate := validator.New(validator.WithRequiredStructEnabled())
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
| credit_card | Credit Card Number |
| mongodb | MongoDB ObjectID |
| mongodb_connection_string | MongoDB Connection String |
| cron | Cron |
| spicedb | SpiceDb ObjectID/Permission/Type |
| datetime | Datetime |
| e164 | e164 formatted phone number |
| ein | U.S. Employeer Identification Number |
| email | E-mail String
| eth_addr | Ethereum Address |
| hexadecimal | Hexadecimal String |
@@ -262,8 +256,6 @@ validate := validator.New(validator.WithRequiredStructEnabled())
| excluded_without | Excluded Without |
| excluded_without_all | Excluded Without All |
| unique | Unique |
| validateFn | Verify if the method `Validate() error` does not return an error (or any specified method) |
#### Aliases:
| Tag | Description |
@@ -273,75 +265,74 @@ validate := validator.New(validator.WithRequiredStructEnabled())
Benchmarks
------
###### Run on MacBook Pro Max M3
###### Run on MacBook Pro (15-inch, 2017) go version go1.10.2 darwin/amd64
```go
go version go1.23.3 darwin/arm64
go version go1.21.0 darwin/arm64
goos: darwin
goarch: arm64
cpu: Apple M3 Max
pkg: github.com/go-playground/validator/v10
BenchmarkFieldSuccess-16 42461943 27.88 ns/op 0 B/op 0 allocs/op
BenchmarkFieldSuccessParallel-16 486632887 2.289 ns/op 0 B/op 0 allocs/op
BenchmarkFieldFailure-16 9566167 121.3 ns/op 200 B/op 4 allocs/op
BenchmarkFieldFailureParallel-16 17551471 83.68 ns/op 200 B/op 4 allocs/op
BenchmarkFieldArrayDiveSuccess-16 7602306 155.6 ns/op 97 B/op 5 allocs/op
BenchmarkFieldArrayDiveSuccessParallel-16 20664610 59.80 ns/op 97 B/op 5 allocs/op
BenchmarkFieldArrayDiveFailure-16 4659756 252.9 ns/op 301 B/op 10 allocs/op
BenchmarkFieldArrayDiveFailureParallel-16 8010116 152.9 ns/op 301 B/op 10 allocs/op
BenchmarkFieldMapDiveSuccess-16 2834575 421.2 ns/op 288 B/op 14 allocs/op
BenchmarkFieldMapDiveSuccessParallel-16 7179700 171.8 ns/op 288 B/op 14 allocs/op
BenchmarkFieldMapDiveFailure-16 3081728 384.4 ns/op 376 B/op 13 allocs/op
BenchmarkFieldMapDiveFailureParallel-16 6058137 204.0 ns/op 377 B/op 13 allocs/op
BenchmarkFieldMapDiveWithKeysSuccess-16 2544975 464.8 ns/op 288 B/op 14 allocs/op
BenchmarkFieldMapDiveWithKeysSuccessParallel-16 6661954 181.4 ns/op 288 B/op 14 allocs/op
BenchmarkFieldMapDiveWithKeysFailure-16 2435484 490.7 ns/op 553 B/op 16 allocs/op
BenchmarkFieldMapDiveWithKeysFailureParallel-16 4249617 282.0 ns/op 554 B/op 16 allocs/op
BenchmarkFieldCustomTypeSuccess-16 14943525 77.35 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeSuccessParallel-16 64051954 20.61 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeFailure-16 10721384 107.1 ns/op 184 B/op 3 allocs/op
BenchmarkFieldCustomTypeFailureParallel-16 18714495 69.77 ns/op 184 B/op 3 allocs/op
BenchmarkFieldOrTagSuccess-16 4063124 294.3 ns/op 16 B/op 1 allocs/op
BenchmarkFieldOrTagSuccessParallel-16 31903756 41.22 ns/op 18 B/op 1 allocs/op
BenchmarkFieldOrTagFailure-16 7748558 146.8 ns/op 216 B/op 5 allocs/op
BenchmarkFieldOrTagFailureParallel-16 13139854 92.05 ns/op 216 B/op 5 allocs/op
BenchmarkStructLevelValidationSuccess-16 16808389 70.25 ns/op 16 B/op 1 allocs/op
BenchmarkStructLevelValidationSuccessParallel-16 90686955 14.47 ns/op 16 B/op 1 allocs/op
BenchmarkStructLevelValidationFailure-16 5818791 200.2 ns/op 264 B/op 7 allocs/op
BenchmarkStructLevelValidationFailureParallel-16 11115874 107.5 ns/op 264 B/op 7 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-16 7764956 151.9 ns/op 32 B/op 2 allocs/op
BenchmarkStructSimpleCustomTypeSuccessParallel-16 52316265 30.37 ns/op 32 B/op 2 allocs/op
BenchmarkStructSimpleCustomTypeFailure-16 4195429 277.2 ns/op 416 B/op 9 allocs/op
BenchmarkStructSimpleCustomTypeFailureParallel-16 7305661 164.6 ns/op 432 B/op 10 allocs/op
BenchmarkStructFilteredSuccess-16 6312625 186.1 ns/op 216 B/op 5 allocs/op
BenchmarkStructFilteredSuccessParallel-16 13684459 93.42 ns/op 216 B/op 5 allocs/op
BenchmarkStructFilteredFailure-16 6751482 171.2 ns/op 216 B/op 5 allocs/op
BenchmarkStructFilteredFailureParallel-16 14146070 86.93 ns/op 216 B/op 5 allocs/op
BenchmarkStructPartialSuccess-16 6544448 177.3 ns/op 224 B/op 4 allocs/op
BenchmarkStructPartialSuccessParallel-16 13951946 88.73 ns/op 224 B/op 4 allocs/op
BenchmarkStructPartialFailure-16 4075833 287.5 ns/op 440 B/op 9 allocs/op
BenchmarkStructPartialFailureParallel-16 7490805 161.3 ns/op 440 B/op 9 allocs/op
BenchmarkStructExceptSuccess-16 4107187 281.4 ns/op 424 B/op 8 allocs/op
BenchmarkStructExceptSuccessParallel-16 15979173 80.86 ns/op 208 B/op 3 allocs/op
BenchmarkStructExceptFailure-16 4434372 264.3 ns/op 424 B/op 8 allocs/op
BenchmarkStructExceptFailureParallel-16 8081367 154.1 ns/op 424 B/op 8 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-16 6459542 183.4 ns/op 56 B/op 3 allocs/op
BenchmarkStructSimpleCrossFieldSuccessParallel-16 41013781 37.95 ns/op 56 B/op 3 allocs/op
BenchmarkStructSimpleCrossFieldFailure-16 4034998 292.1 ns/op 272 B/op 8 allocs/op
BenchmarkStructSimpleCrossFieldFailureParallel-16 11348446 115.3 ns/op 272 B/op 8 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-16 4448528 267.7 ns/op 64 B/op 4 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-16 26813619 48.33 ns/op 64 B/op 4 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-16 3090646 384.5 ns/op 288 B/op 9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-16 9870906 129.5 ns/op 288 B/op 9 allocs/op
BenchmarkStructSimpleSuccess-16 10675562 109.5 ns/op 0 B/op 0 allocs/op
BenchmarkStructSimpleSuccessParallel-16 131159784 8.932 ns/op 0 B/op 0 allocs/op
BenchmarkStructSimpleFailure-16 4094979 286.6 ns/op 416 B/op 9 allocs/op
BenchmarkStructSimpleFailureParallel-16 7606663 157.9 ns/op 416 B/op 9 allocs/op
BenchmarkStructComplexSuccess-16 2073470 576.0 ns/op 224 B/op 5 allocs/op
BenchmarkStructComplexSuccessParallel-16 7821831 161.3 ns/op 224 B/op 5 allocs/op
BenchmarkStructComplexFailure-16 576358 2001 ns/op 3042 B/op 48 allocs/op
BenchmarkStructComplexFailureParallel-16 1000000 1171 ns/op 3041 B/op 48 allocs/op
BenchmarkOneof-16 22503973 52.82 ns/op 0 B/op 0 allocs/op
BenchmarkOneofParallel-16 8538474 140.4 ns/op 0 B/op 0 allocs/op
BenchmarkFieldSuccess-8 33142266 35.94 ns/op 0 B/op 0 allocs/op
BenchmarkFieldSuccessParallel-8 200816191 6.568 ns/op 0 B/op 0 allocs/op
BenchmarkFieldFailure-8 6779707 175.1 ns/op 200 B/op 4 allocs/op
BenchmarkFieldFailureParallel-8 11044147 108.4 ns/op 200 B/op 4 allocs/op
BenchmarkFieldArrayDiveSuccess-8 6054232 194.4 ns/op 97 B/op 5 allocs/op
BenchmarkFieldArrayDiveSuccessParallel-8 12523388 94.07 ns/op 97 B/op 5 allocs/op
BenchmarkFieldArrayDiveFailure-8 3587043 334.3 ns/op 300 B/op 10 allocs/op
BenchmarkFieldArrayDiveFailureParallel-8 5816665 200.8 ns/op 300 B/op 10 allocs/op
BenchmarkFieldMapDiveSuccess-8 2217910 540.1 ns/op 288 B/op 14 allocs/op
BenchmarkFieldMapDiveSuccessParallel-8 4446698 258.7 ns/op 288 B/op 14 allocs/op
BenchmarkFieldMapDiveFailure-8 2392759 504.6 ns/op 376 B/op 13 allocs/op
BenchmarkFieldMapDiveFailureParallel-8 4244199 286.9 ns/op 376 B/op 13 allocs/op
BenchmarkFieldMapDiveWithKeysSuccess-8 2005857 592.1 ns/op 288 B/op 14 allocs/op
BenchmarkFieldMapDiveWithKeysSuccessParallel-8 4400850 296.9 ns/op 288 B/op 14 allocs/op
BenchmarkFieldMapDiveWithKeysFailure-8 1850227 643.8 ns/op 553 B/op 16 allocs/op
BenchmarkFieldMapDiveWithKeysFailureParallel-8 3293233 375.1 ns/op 553 B/op 16 allocs/op
BenchmarkFieldCustomTypeSuccess-8 12174412 98.25 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeSuccessParallel-8 34389907 35.49 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeFailure-8 7582524 156.6 ns/op 184 B/op 3 allocs/op
BenchmarkFieldCustomTypeFailureParallel-8 13019902 92.79 ns/op 184 B/op 3 allocs/op
BenchmarkFieldOrTagSuccess-8 3427260 349.4 ns/op 16 B/op 1 allocs/op
BenchmarkFieldOrTagSuccessParallel-8 15144128 81.25 ns/op 16 B/op 1 allocs/op
BenchmarkFieldOrTagFailure-8 5913546 201.9 ns/op 216 B/op 5 allocs/op
BenchmarkFieldOrTagFailureParallel-8 9810212 113.7 ns/op 216 B/op 5 allocs/op
BenchmarkStructLevelValidationSuccess-8 13456327 87.66 ns/op 16 B/op 1 allocs/op
BenchmarkStructLevelValidationSuccessParallel-8 41818888 27.77 ns/op 16 B/op 1 allocs/op
BenchmarkStructLevelValidationFailure-8 4166284 272.6 ns/op 264 B/op 7 allocs/op
BenchmarkStructLevelValidationFailureParallel-8 7594581 152.1 ns/op 264 B/op 7 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-8 6508082 182.6 ns/op 32 B/op 2 allocs/op
BenchmarkStructSimpleCustomTypeSuccessParallel-8 23078605 54.78 ns/op 32 B/op 2 allocs/op
BenchmarkStructSimpleCustomTypeFailure-8 3118352 381.0 ns/op 416 B/op 9 allocs/op
BenchmarkStructSimpleCustomTypeFailureParallel-8 5300738 224.1 ns/op 432 B/op 10 allocs/op
BenchmarkStructFilteredSuccess-8 4761807 251.1 ns/op 216 B/op 5 allocs/op
BenchmarkStructFilteredSuccessParallel-8 8792598 128.6 ns/op 216 B/op 5 allocs/op
BenchmarkStructFilteredFailure-8 5202573 232.1 ns/op 216 B/op 5 allocs/op
BenchmarkStructFilteredFailureParallel-8 9591267 121.4 ns/op 216 B/op 5 allocs/op
BenchmarkStructPartialSuccess-8 5188512 231.6 ns/op 224 B/op 4 allocs/op
BenchmarkStructPartialSuccessParallel-8 9179776 123.1 ns/op 224 B/op 4 allocs/op
BenchmarkStructPartialFailure-8 3071212 392.5 ns/op 440 B/op 9 allocs/op
BenchmarkStructPartialFailureParallel-8 5344261 223.7 ns/op 440 B/op 9 allocs/op
BenchmarkStructExceptSuccess-8 3184230 375.0 ns/op 424 B/op 8 allocs/op
BenchmarkStructExceptSuccessParallel-8 10090130 108.9 ns/op 208 B/op 3 allocs/op
BenchmarkStructExceptFailure-8 3347226 357.7 ns/op 424 B/op 8 allocs/op
BenchmarkStructExceptFailureParallel-8 5654923 209.5 ns/op 424 B/op 8 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-8 5232265 229.1 ns/op 56 B/op 3 allocs/op
BenchmarkStructSimpleCrossFieldSuccessParallel-8 17436674 64.75 ns/op 56 B/op 3 allocs/op
BenchmarkStructSimpleCrossFieldFailure-8 3128613 383.6 ns/op 272 B/op 8 allocs/op
BenchmarkStructSimpleCrossFieldFailureParallel-8 6994113 168.8 ns/op 272 B/op 8 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 3506487 340.9 ns/op 64 B/op 4 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 13431300 91.77 ns/op 64 B/op 4 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2410566 500.9 ns/op 288 B/op 9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 6344510 188.2 ns/op 288 B/op 9 allocs/op
BenchmarkStructSimpleSuccess-8 8922726 133.8 ns/op 0 B/op 0 allocs/op
BenchmarkStructSimpleSuccessParallel-8 55291153 23.63 ns/op 0 B/op 0 allocs/op
BenchmarkStructSimpleFailure-8 3171553 378.4 ns/op 416 B/op 9 allocs/op
BenchmarkStructSimpleFailureParallel-8 5571692 212.0 ns/op 416 B/op 9 allocs/op
BenchmarkStructComplexSuccess-8 1683750 714.5 ns/op 224 B/op 5 allocs/op
BenchmarkStructComplexSuccessParallel-8 4578046 257.0 ns/op 224 B/op 5 allocs/op
BenchmarkStructComplexFailure-8 481585 2547 ns/op 3041 B/op 48 allocs/op
BenchmarkStructComplexFailureParallel-8 965764 1577 ns/op 3040 B/op 48 allocs/op
BenchmarkOneof-8 17380881 68.50 ns/op 0 B/op 0 allocs/op
BenchmarkOneofParallel-8 8084733 153.5 ns/op 0 B/op 0 allocs/op
```
Complementary Software
@@ -357,20 +348,6 @@ How to Contribute
Make a pull request...
Maintenance and support for SDK major versions
----------------------------------------------
See prior discussion [here](https://github.com/go-playground/validator/discussions/1342) for more details.
This package is aligned with the [Go release policy](https://go.dev/doc/devel/release) in that support is guaranteed for
the two most recent major versions.
This does not mean the package will not work with older versions of Go, only that we reserve the right to increase the
MSGV(Minimum Supported Go Version) when the need arises to address Security issues/patches, OS issues & support or newly
introduced functionality that would greatly benefit the maintenance and/or usage of this package.
If and when the MSGV is increased it will be done so in a minimum of a `Minor` release bump.
License
-------
Distributed under MIT License, please see license file within the code for more details.

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,6 @@ const (
typeKeys
typeEndKeys
typeOmitNil
typeOmitZero
)
const (
@@ -124,6 +123,7 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
var customName string
for i := 0; i < numFields; i++ {
fld = typ.Field(i)
if !v.privateFieldValidation && !fld.Anonymous && len(fld.PkgPath) > 0 {
@@ -190,6 +190,7 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
} else {
next, curr := v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
current.next, current = next, curr
}
continue
}
@@ -208,6 +209,7 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
switch t {
case diveTag:
current.typeof = typeDive
continue
case keysTag:
current.typeof = typeKeys
@@ -216,6 +218,8 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
panic(fmt.Sprintf("'%s' tag must be immediately preceded by the '%s' tag", keysTag, diveTag))
}
current.typeof = typeKeys
// need to pass along only keys tag
// need to increment i to skip over the keys tags
b := make([]byte, 0, 64)
@@ -223,6 +227,7 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
i++
for ; i < len(tags); i++ {
b = append(b, tags[i]...)
b = append(b, ',')
@@ -232,6 +237,7 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
}
current.keys, _ = v.parseFieldTagsRecursive(string(b[:len(b)-1]), fieldName, "", false)
continue
case endKeysTag:
current.typeof = typeEndKeys
@@ -243,21 +249,21 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
}
return
case omitzero:
current.typeof = typeOmitZero
continue
case omitempty:
current.typeof = typeOmitEmpty
continue
case omitnil:
current.typeof = typeOmitNil
continue
case structOnlyTag:
current.typeof = typeStructOnly
continue
case noStructLevelTag:
current.typeof = typeNoStructLevel
continue
default:
if t == isdefault {
@@ -288,7 +294,7 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
if wrapper, ok := v.validations[current.tag]; ok {
current.fn = wrapper.fn
current.runValidationWhenNil = wrapper.runValidationOnNil
current.runValidationWhenNil = wrapper.runValidatinOnNil
} else {
panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, current.tag, fieldName)))
}
@@ -298,7 +304,7 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
}
if len(vals) > 1 {
current.param = strings.ReplaceAll(strings.ReplaceAll(vals[1], utf8HexComma, ","), utf8Pipe, "|")
current.param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1)
}
}
current.isBlockEnd = true

File diff suppressed because it is too large Load Diff

View File

@@ -1,79 +1,79 @@
package validator
var iso4217 = map[string]struct{}{
"AFN": {}, "EUR": {}, "ALL": {}, "DZD": {}, "USD": {},
"AOA": {}, "XCD": {}, "ARS": {}, "AMD": {}, "AWG": {},
"AUD": {}, "AZN": {}, "BSD": {}, "BHD": {}, "BDT": {},
"BBD": {}, "BYN": {}, "BZD": {}, "XOF": {}, "BMD": {},
"INR": {}, "BTN": {}, "BOB": {}, "BOV": {}, "BAM": {},
"BWP": {}, "NOK": {}, "BRL": {}, "BND": {}, "BGN": {},
"BIF": {}, "CVE": {}, "KHR": {}, "XAF": {}, "CAD": {},
"KYD": {}, "CLP": {}, "CLF": {}, "CNY": {}, "COP": {},
"COU": {}, "KMF": {}, "CDF": {}, "NZD": {}, "CRC": {},
"HRK": {}, "CUP": {}, "CUC": {}, "ANG": {}, "CZK": {},
"DKK": {}, "DJF": {}, "DOP": {}, "EGP": {}, "SVC": {},
"ERN": {}, "SZL": {}, "ETB": {}, "FKP": {}, "FJD": {},
"XPF": {}, "GMD": {}, "GEL": {}, "GHS": {}, "GIP": {},
"GTQ": {}, "GBP": {}, "GNF": {}, "GYD": {}, "HTG": {},
"HNL": {}, "HKD": {}, "HUF": {}, "ISK": {}, "IDR": {},
"XDR": {}, "IRR": {}, "IQD": {}, "ILS": {}, "JMD": {},
"JPY": {}, "JOD": {}, "KZT": {}, "KES": {}, "KPW": {},
"KRW": {}, "KWD": {}, "KGS": {}, "LAK": {}, "LBP": {},
"LSL": {}, "ZAR": {}, "LRD": {}, "LYD": {}, "CHF": {},
"MOP": {}, "MKD": {}, "MGA": {}, "MWK": {}, "MYR": {},
"MVR": {}, "MRU": {}, "MUR": {}, "XUA": {}, "MXN": {},
"MXV": {}, "MDL": {}, "MNT": {}, "MAD": {}, "MZN": {},
"MMK": {}, "NAD": {}, "NPR": {}, "NIO": {}, "NGN": {},
"OMR": {}, "PKR": {}, "PAB": {}, "PGK": {}, "PYG": {},
"PEN": {}, "PHP": {}, "PLN": {}, "QAR": {}, "RON": {},
"RUB": {}, "RWF": {}, "SHP": {}, "WST": {}, "STN": {},
"SAR": {}, "RSD": {}, "SCR": {}, "SLL": {}, "SGD": {},
"XSU": {}, "SBD": {}, "SOS": {}, "SSP": {}, "LKR": {},
"SDG": {}, "SRD": {}, "SEK": {}, "CHE": {}, "CHW": {},
"SYP": {}, "TWD": {}, "TJS": {}, "TZS": {}, "THB": {},
"TOP": {}, "TTD": {}, "TND": {}, "TRY": {}, "TMT": {},
"UGX": {}, "UAH": {}, "AED": {}, "USN": {}, "UYU": {},
"UYI": {}, "UYW": {}, "UZS": {}, "VUV": {}, "VES": {},
"VND": {}, "YER": {}, "ZMW": {}, "ZWL": {}, "XBA": {},
"XBB": {}, "XBC": {}, "XBD": {}, "XTS": {}, "XXX": {},
"XAU": {}, "XPD": {}, "XPT": {}, "XAG": {},
var iso4217 = map[string]bool{
"AFN": true, "EUR": true, "ALL": true, "DZD": true, "USD": true,
"AOA": true, "XCD": true, "ARS": true, "AMD": true, "AWG": true,
"AUD": true, "AZN": true, "BSD": true, "BHD": true, "BDT": true,
"BBD": true, "BYN": true, "BZD": true, "XOF": true, "BMD": true,
"INR": true, "BTN": true, "BOB": true, "BOV": true, "BAM": true,
"BWP": true, "NOK": true, "BRL": true, "BND": true, "BGN": true,
"BIF": true, "CVE": true, "KHR": true, "XAF": true, "CAD": true,
"KYD": true, "CLP": true, "CLF": true, "CNY": true, "COP": true,
"COU": true, "KMF": true, "CDF": true, "NZD": true, "CRC": true,
"HRK": true, "CUP": true, "CUC": true, "ANG": true, "CZK": true,
"DKK": true, "DJF": true, "DOP": true, "EGP": true, "SVC": true,
"ERN": true, "SZL": true, "ETB": true, "FKP": true, "FJD": true,
"XPF": true, "GMD": true, "GEL": true, "GHS": true, "GIP": true,
"GTQ": true, "GBP": true, "GNF": true, "GYD": true, "HTG": true,
"HNL": true, "HKD": true, "HUF": true, "ISK": true, "IDR": true,
"XDR": true, "IRR": true, "IQD": true, "ILS": true, "JMD": true,
"JPY": true, "JOD": true, "KZT": true, "KES": true, "KPW": true,
"KRW": true, "KWD": true, "KGS": true, "LAK": true, "LBP": true,
"LSL": true, "ZAR": true, "LRD": true, "LYD": true, "CHF": true,
"MOP": true, "MKD": true, "MGA": true, "MWK": true, "MYR": true,
"MVR": true, "MRU": true, "MUR": true, "XUA": true, "MXN": true,
"MXV": true, "MDL": true, "MNT": true, "MAD": true, "MZN": true,
"MMK": true, "NAD": true, "NPR": true, "NIO": true, "NGN": true,
"OMR": true, "PKR": true, "PAB": true, "PGK": true, "PYG": true,
"PEN": true, "PHP": true, "PLN": true, "QAR": true, "RON": true,
"RUB": true, "RWF": true, "SHP": true, "WST": true, "STN": true,
"SAR": true, "RSD": true, "SCR": true, "SLL": true, "SGD": true,
"XSU": true, "SBD": true, "SOS": true, "SSP": true, "LKR": true,
"SDG": true, "SRD": true, "SEK": true, "CHE": true, "CHW": true,
"SYP": true, "TWD": true, "TJS": true, "TZS": true, "THB": true,
"TOP": true, "TTD": true, "TND": true, "TRY": true, "TMT": true,
"UGX": true, "UAH": true, "AED": true, "USN": true, "UYU": true,
"UYI": true, "UYW": true, "UZS": true, "VUV": true, "VES": true,
"VND": true, "YER": true, "ZMW": true, "ZWL": true, "XBA": true,
"XBB": true, "XBC": true, "XBD": true, "XTS": true, "XXX": true,
"XAU": true, "XPD": true, "XPT": true, "XAG": true,
}
var iso4217_numeric = map[int]struct{}{
8: {}, 12: {}, 32: {}, 36: {}, 44: {},
48: {}, 50: {}, 51: {}, 52: {}, 60: {},
64: {}, 68: {}, 72: {}, 84: {}, 90: {},
96: {}, 104: {}, 108: {}, 116: {}, 124: {},
132: {}, 136: {}, 144: {}, 152: {}, 156: {},
170: {}, 174: {}, 188: {}, 191: {}, 192: {},
203: {}, 208: {}, 214: {}, 222: {}, 230: {},
232: {}, 238: {}, 242: {}, 262: {}, 270: {},
292: {}, 320: {}, 324: {}, 328: {}, 332: {},
340: {}, 344: {}, 348: {}, 352: {}, 356: {},
360: {}, 364: {}, 368: {}, 376: {}, 388: {},
392: {}, 398: {}, 400: {}, 404: {}, 408: {},
410: {}, 414: {}, 417: {}, 418: {}, 422: {},
426: {}, 430: {}, 434: {}, 446: {}, 454: {},
458: {}, 462: {}, 480: {}, 484: {}, 496: {},
498: {}, 504: {}, 512: {}, 516: {}, 524: {},
532: {}, 533: {}, 548: {}, 554: {}, 558: {},
566: {}, 578: {}, 586: {}, 590: {}, 598: {},
600: {}, 604: {}, 608: {}, 634: {}, 643: {},
646: {}, 654: {}, 682: {}, 690: {}, 694: {},
702: {}, 704: {}, 706: {}, 710: {}, 728: {},
748: {}, 752: {}, 756: {}, 760: {}, 764: {},
776: {}, 780: {}, 784: {}, 788: {}, 800: {},
807: {}, 818: {}, 826: {}, 834: {}, 840: {},
858: {}, 860: {}, 882: {}, 886: {}, 901: {},
927: {}, 928: {}, 929: {}, 930: {}, 931: {},
932: {}, 933: {}, 934: {}, 936: {}, 938: {},
940: {}, 941: {}, 943: {}, 944: {}, 946: {},
947: {}, 948: {}, 949: {}, 950: {}, 951: {},
952: {}, 953: {}, 955: {}, 956: {}, 957: {},
958: {}, 959: {}, 960: {}, 961: {}, 962: {},
963: {}, 964: {}, 965: {}, 967: {}, 968: {},
969: {}, 970: {}, 971: {}, 972: {}, 973: {},
975: {}, 976: {}, 977: {}, 978: {}, 979: {},
980: {}, 981: {}, 984: {}, 985: {}, 986: {},
990: {}, 994: {}, 997: {}, 999: {},
var iso4217_numeric = map[int]bool{
8: true, 12: true, 32: true, 36: true, 44: true,
48: true, 50: true, 51: true, 52: true, 60: true,
64: true, 68: true, 72: true, 84: true, 90: true,
96: true, 104: true, 108: true, 116: true, 124: true,
132: true, 136: true, 144: true, 152: true, 156: true,
170: true, 174: true, 188: true, 191: true, 192: true,
203: true, 208: true, 214: true, 222: true, 230: true,
232: true, 238: true, 242: true, 262: true, 270: true,
292: true, 320: true, 324: true, 328: true, 332: true,
340: true, 344: true, 348: true, 352: true, 356: true,
360: true, 364: true, 368: true, 376: true, 388: true,
392: true, 398: true, 400: true, 404: true, 408: true,
410: true, 414: true, 417: true, 418: true, 422: true,
426: true, 430: true, 434: true, 446: true, 454: true,
458: true, 462: true, 480: true, 484: true, 496: true,
498: true, 504: true, 512: true, 516: true, 524: true,
532: true, 533: true, 548: true, 554: true, 558: true,
566: true, 578: true, 586: true, 590: true, 598: true,
600: true, 604: true, 608: true, 634: true, 643: true,
646: true, 654: true, 682: true, 690: true, 694: true,
702: true, 704: true, 706: true, 710: true, 728: true,
748: true, 752: true, 756: true, 760: true, 764: true,
776: true, 780: true, 784: true, 788: true, 800: true,
807: true, 818: true, 826: true, 834: true, 840: true,
858: true, 860: true, 882: true, 886: true, 901: true,
927: true, 928: true, 929: true, 930: true, 931: true,
932: true, 933: true, 934: true, 936: true, 938: true,
940: true, 941: true, 943: true, 944: true, 946: true,
947: true, 948: true, 949: true, 950: true, 951: true,
952: true, 953: true, 955: true, 956: true, 957: true,
958: true, 959: true, 960: true, 961: true, 962: true,
963: true, 964: true, 965: true, 967: true, 968: true,
969: true, 970: true, 971: true, 972: true, 973: true,
975: true, 976: true, 977: true, 978: true, 979: true,
980: true, 981: true, 984: true, 985: true, 986: true,
990: true, 994: true, 997: true, 999: true,
}

View File

@@ -188,7 +188,7 @@ Same as structonly tag except that any struct level validations will not run.
# Omit Empty
Allows conditional validation, for example, if a field is not set with
Allows conditional validation, for example if a field is not set with
a value (Determined by the "required" validator) then other validation
such as min or max won't run, but if a value is set validation will run.
@@ -253,7 +253,7 @@ Example #2
This validates that the value is not the data types default zero value.
For numbers ensures value is not zero. For strings ensures value is
not "". For booleans ensures value is not false. For slices, maps, pointers, interfaces, channels and functions
not "". For slices, maps, pointers, interfaces, channels and functions
ensures the value is not nil. For structs ensures value is not the zero value when using WithRequiredStructEnabled.
Usage: required
@@ -489,19 +489,12 @@ For strings, ints, and uints, oneof will ensure that the value
is one of the values in the parameter. The parameter should be
a list of values separated by whitespace. Values may be
strings or numbers. To match strings with spaces in them, include
the target string between single quotes. Kind of like an 'enum'.
the target string between single quotes.
Usage: oneof=red green
oneof='red green' 'blue yellow'
oneof=5 7 9
# One Of Case Insensitive
Works the same as oneof but is case insensitive and therefore only accepts strings.
Usage: oneofci=red green
oneofci='red green' 'blue yellow'
# Greater Than
For numbers, this will ensure that the value is greater than the
@@ -756,20 +749,6 @@ in a field of the struct specified via a parameter.
// For slices of struct:
Usage: unique=field
# ValidateFn
This validates that an object responds to a method that can return error or bool.
By default it expects an interface `Validate() error` and check that the method
does not return an error. Other methods can be specified using two signatures:
If the method returns an error, it check if the return value is nil.
If the method returns a boolean, it checks if the value is true.
// to use the default method Validate() error
Usage: validateFn
// to use the custom method IsValid() bool (or error)
Usage: validateFn=IsValid
# Alpha Only
This validates that a string value contains ASCII alpha characters only
@@ -932,7 +911,7 @@ This will accept any uri the golang request uri accepts
# Urn RFC 2141 String
This validates that a string value contains a valid URN
This validataes that a string value contains a valid URN
according to the RFC 2141 spec.
Usage: urn_rfc2141
@@ -973,7 +952,7 @@ Although an empty string is a valid base64 URL safe value, this will report
an empty string as an error, if you wish to accept an empty string as valid
you can use this with the omitempty tag.
Usage: base64rawurl
Usage: base64url
# Bitcoin Address
@@ -987,7 +966,7 @@ Bitcoin Bech32 Address (segwit)
This validates that a string value contains a valid bitcoin Bech32 address as defined
by bip-0173 (https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)
Special thanks to Pieter Wuille for providing reference implementations.
Special thanks to Pieter Wuille for providng reference implementations.
Usage: btc_addr_bech32
@@ -1148,12 +1127,6 @@ This validates that a string value contains a valid longitude.
Usage: longitude
# Employeer Identification Number EIN
This validates that a string value contains a valid U.S. Employer Identification Number.
Usage: ein
# Social Security Number SSN
This validates that a string value contains a valid U.S. Social Security Number.
@@ -1326,7 +1299,7 @@ may not exist at the time of validation.
# HostPort
This validates that a string value contains a valid DNS hostname and port that
can be used to validate fields typically passed to sockets and connections.
can be used to valiate fields typically passed to sockets and connections.
Usage: hostname_port
@@ -1413,19 +1386,11 @@ This validates that a string value contains a valid credit card number using Luh
This validates that a string or (u)int value contains a valid checksum using the Luhn algorithm.
# MongoDB
# MongoDb ObjectID
This validates that a string is a valid 24 character hexadecimal string or valid connection string.
This validates that a string is a valid 24 character hexadecimal string.
Usage: mongodb
mongodb_connection_string
Example:
type Test struct {
ObjectIdField string `validate:"mongodb"`
ConnectionStringField string `validate:"mongodb_connection_string"`
}
# Cron

View File

@@ -24,6 +24,7 @@ type InvalidValidationError struct {
// Error returns InvalidValidationError message
func (e *InvalidValidationError) Error() string {
if e.Type == nil {
return "validator: (nil)"
}
@@ -40,9 +41,11 @@ type ValidationErrors []FieldError
// All information to create an error message specific to your application is contained within
// the FieldError found within the ValidationErrors array
func (ve ValidationErrors) Error() string {
buff := bytes.NewBufferString("")
for i := 0; i < len(ve); i++ {
buff.WriteString(ve[i].Error())
buff.WriteString("\n")
}
@@ -52,6 +55,7 @@ func (ve ValidationErrors) Error() string {
// Translate translates all of the ValidationErrors
func (ve ValidationErrors) Translate(ut ut.Translator) ValidationErrorsTranslations {
trans := make(ValidationErrorsTranslations)
var fe *fieldError
@@ -105,24 +109,22 @@ type FieldError interface {
// StructNamespace returns the namespace for the field error, with the field's
// actual name.
//
// eg. "User.FirstName" see Namespace for comparison
// eq. "User.FirstName" see Namespace for comparison
//
// NOTE: this field can be blank when validating a single primitive field
// using validate.Field(...) as there is no way to extract its name
StructNamespace() string
// Field returns the field's name with the tag name taking precedence over the
// Field returns the fields name with the tag name taking precedence over the
// field's actual name.
//
// `RegisterTagNameFunc` must be registered to get tag value.
//
// eg. JSON name "fname"
// eq. JSON name "fname"
// see StructField for comparison
Field() string
// StructField returns the field's actual name from the struct, when able to determine.
//
// eg. "FirstName"
// eq. "FirstName"
// see Field for comparison
StructField() string
@@ -202,6 +204,7 @@ func (fe *fieldError) StructNamespace() string {
// Field returns the field's name with the tag name taking precedence over the
// field's actual name.
func (fe *fieldError) Field() string {
return fe.ns[len(fe.ns)-int(fe.fieldLen):]
// // return fe.field
// fld := fe.ns[len(fe.ns)-int(fe.fieldLen):]

View File

@@ -1,9 +1,6 @@
package validator
import (
"regexp"
"sync"
)
import "regexp"
var postCodePatternDict = map[string]string{
"GB": `^GIR[ ]?0AA|((AB|AL|B|BA|BB|BD|BH|BL|BN|BR|BS|BT|CA|CB|CF|CH|CM|CO|CR|CT|CV|CW|DA|DD|DE|DG|DH|DL|DN|DT|DY|E|EC|EH|EN|EX|FK|FY|G|GL|GY|GU|HA|HD|HG|HP|HR|HS|HU|HX|IG|IM|IP|IV|JE|KA|KT|KW|KY|L|LA|LD|LE|LL|LN|LS|LU|M|ME|MK|ML|N|NE|NG|NN|NP|NR|NW|OL|OX|PA|PE|PH|PL|PO|PR|RG|RH|RM|S|SA|SE|SG|SK|SL|SM|SN|SO|SP|SR|SS|ST|SW|SY|TA|TD|TF|TN|TQ|TR|TS|TW|UB|W|WA|WC|WD|WF|WN|WR|WS|WV|YO|ZE)(\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}))|BFPO[ ]?\d{1,4}$`,
@@ -167,12 +164,9 @@ var postCodePatternDict = map[string]string{
"YT": `^976\d{2}$`,
}
var (
postcodeRegexInit sync.Once
postCodeRegexDict = map[string]*regexp.Regexp{}
)
var postCodeRegexDict = map[string]*regexp.Regexp{}
func initPostcodes() {
func init() {
for countryCode, pattern := range postCodePatternDict {
postCodeRegexDict[countryCode] = regexp.MustCompile(pattern)
}

View File

@@ -1,9 +1,6 @@
package validator
import (
"regexp"
"sync"
)
import "regexp"
const (
alphaRegexString = "^[a-zA-Z]+$"
@@ -69,97 +66,82 @@ const (
splitParamsRegexString = `'[^']*'|\S+`
bicRegexString = `^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$`
semverRegexString = `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$` // numbered capture groups https://semver.org/
dnsRegexStringRFC1035Label = "^[a-z]([-a-z0-9]*[a-z0-9])?$"
dnsRegexStringRFC1035Label = "^[a-z]([-a-z0-9]*[a-z0-9]){0,62}$"
cveRegexString = `^CVE-(1999|2\d{3})-(0[^0]\d{2}|0\d[^0]\d{1}|0\d{2}[^0]|[1-9]{1}\d{3,})$` // CVE Format Id https://cve.mitre.org/cve/identifiers/syntaxchange.html
mongodbIdRegexString = "^[a-f\\d]{24}$"
mongodbConnStringRegexString = "^mongodb(\\+srv)?:\\/\\/(([a-zA-Z\\d]+):([a-zA-Z\\d$:\\/?#\\[\\]@]+)@)?(([a-z\\d.-]+)(:[\\d]+)?)((,(([a-z\\d.-]+)(:(\\d+))?))*)?(\\/[a-zA-Z-_]{1,64})?(\\?(([a-zA-Z]+)=([a-zA-Z\\d]+))(&(([a-zA-Z\\d]+)=([a-zA-Z\\d]+))?)*)?$"
cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|((\*|\d+)(\/|-)\d+)|\d+|\*) ?){5,7})`
mongodbRegexString = "^[a-f\\d]{24}$"
cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})`
spicedbIDRegexString = `^(([a-zA-Z0-9/_|\-=+]{1,})|\*)$`
spicedbPermissionRegexString = "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$"
spicedbTypeRegexString = "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$"
einRegexString = "^(\\d{2}-\\d{7})$"
)
func lazyRegexCompile(str string) func() *regexp.Regexp {
var regex *regexp.Regexp
var once sync.Once
return func() *regexp.Regexp {
once.Do(func() {
regex = regexp.MustCompile(str)
})
return regex
}
}
var (
alphaRegex = lazyRegexCompile(alphaRegexString)
alphaNumericRegex = lazyRegexCompile(alphaNumericRegexString)
alphaUnicodeRegex = lazyRegexCompile(alphaUnicodeRegexString)
alphaUnicodeNumericRegex = lazyRegexCompile(alphaUnicodeNumericRegexString)
numericRegex = lazyRegexCompile(numericRegexString)
numberRegex = lazyRegexCompile(numberRegexString)
hexadecimalRegex = lazyRegexCompile(hexadecimalRegexString)
hexColorRegex = lazyRegexCompile(hexColorRegexString)
rgbRegex = lazyRegexCompile(rgbRegexString)
rgbaRegex = lazyRegexCompile(rgbaRegexString)
hslRegex = lazyRegexCompile(hslRegexString)
hslaRegex = lazyRegexCompile(hslaRegexString)
e164Regex = lazyRegexCompile(e164RegexString)
emailRegex = lazyRegexCompile(emailRegexString)
base32Regex = lazyRegexCompile(base32RegexString)
base64Regex = lazyRegexCompile(base64RegexString)
base64URLRegex = lazyRegexCompile(base64URLRegexString)
base64RawURLRegex = lazyRegexCompile(base64RawURLRegexString)
iSBN10Regex = lazyRegexCompile(iSBN10RegexString)
iSBN13Regex = lazyRegexCompile(iSBN13RegexString)
iSSNRegex = lazyRegexCompile(iSSNRegexString)
uUID3Regex = lazyRegexCompile(uUID3RegexString)
uUID4Regex = lazyRegexCompile(uUID4RegexString)
uUID5Regex = lazyRegexCompile(uUID5RegexString)
uUIDRegex = lazyRegexCompile(uUIDRegexString)
uUID3RFC4122Regex = lazyRegexCompile(uUID3RFC4122RegexString)
uUID4RFC4122Regex = lazyRegexCompile(uUID4RFC4122RegexString)
uUID5RFC4122Regex = lazyRegexCompile(uUID5RFC4122RegexString)
uUIDRFC4122Regex = lazyRegexCompile(uUIDRFC4122RegexString)
uLIDRegex = lazyRegexCompile(uLIDRegexString)
md4Regex = lazyRegexCompile(md4RegexString)
md5Regex = lazyRegexCompile(md5RegexString)
sha256Regex = lazyRegexCompile(sha256RegexString)
sha384Regex = lazyRegexCompile(sha384RegexString)
sha512Regex = lazyRegexCompile(sha512RegexString)
ripemd128Regex = lazyRegexCompile(ripemd128RegexString)
ripemd160Regex = lazyRegexCompile(ripemd160RegexString)
tiger128Regex = lazyRegexCompile(tiger128RegexString)
tiger160Regex = lazyRegexCompile(tiger160RegexString)
tiger192Regex = lazyRegexCompile(tiger192RegexString)
aSCIIRegex = lazyRegexCompile(aSCIIRegexString)
printableASCIIRegex = lazyRegexCompile(printableASCIIRegexString)
multibyteRegex = lazyRegexCompile(multibyteRegexString)
dataURIRegex = lazyRegexCompile(dataURIRegexString)
latitudeRegex = lazyRegexCompile(latitudeRegexString)
longitudeRegex = lazyRegexCompile(longitudeRegexString)
sSNRegex = lazyRegexCompile(sSNRegexString)
hostnameRegexRFC952 = lazyRegexCompile(hostnameRegexStringRFC952)
hostnameRegexRFC1123 = lazyRegexCompile(hostnameRegexStringRFC1123)
fqdnRegexRFC1123 = lazyRegexCompile(fqdnRegexStringRFC1123)
btcAddressRegex = lazyRegexCompile(btcAddressRegexString)
btcUpperAddressRegexBech32 = lazyRegexCompile(btcAddressUpperRegexStringBech32)
btcLowerAddressRegexBech32 = lazyRegexCompile(btcAddressLowerRegexStringBech32)
ethAddressRegex = lazyRegexCompile(ethAddressRegexString)
uRLEncodedRegex = lazyRegexCompile(uRLEncodedRegexString)
hTMLEncodedRegex = lazyRegexCompile(hTMLEncodedRegexString)
hTMLRegex = lazyRegexCompile(hTMLRegexString)
jWTRegex = lazyRegexCompile(jWTRegexString)
splitParamsRegex = lazyRegexCompile(splitParamsRegexString)
bicRegex = lazyRegexCompile(bicRegexString)
semverRegex = lazyRegexCompile(semverRegexString)
dnsRegexRFC1035Label = lazyRegexCompile(dnsRegexStringRFC1035Label)
cveRegex = lazyRegexCompile(cveRegexString)
mongodbIdRegex = lazyRegexCompile(mongodbIdRegexString)
mongodbConnectionRegex = lazyRegexCompile(mongodbConnStringRegexString)
cronRegex = lazyRegexCompile(cronRegexString)
spicedbIDRegex = lazyRegexCompile(spicedbIDRegexString)
spicedbPermissionRegex = lazyRegexCompile(spicedbPermissionRegexString)
spicedbTypeRegex = lazyRegexCompile(spicedbTypeRegexString)
einRegex = lazyRegexCompile(einRegexString)
alphaRegex = regexp.MustCompile(alphaRegexString)
alphaNumericRegex = regexp.MustCompile(alphaNumericRegexString)
alphaUnicodeRegex = regexp.MustCompile(alphaUnicodeRegexString)
alphaUnicodeNumericRegex = regexp.MustCompile(alphaUnicodeNumericRegexString)
numericRegex = regexp.MustCompile(numericRegexString)
numberRegex = regexp.MustCompile(numberRegexString)
hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString)
hexColorRegex = regexp.MustCompile(hexColorRegexString)
rgbRegex = regexp.MustCompile(rgbRegexString)
rgbaRegex = regexp.MustCompile(rgbaRegexString)
hslRegex = regexp.MustCompile(hslRegexString)
hslaRegex = regexp.MustCompile(hslaRegexString)
e164Regex = regexp.MustCompile(e164RegexString)
emailRegex = regexp.MustCompile(emailRegexString)
base32Regex = regexp.MustCompile(base32RegexString)
base64Regex = regexp.MustCompile(base64RegexString)
base64URLRegex = regexp.MustCompile(base64URLRegexString)
base64RawURLRegex = regexp.MustCompile(base64RawURLRegexString)
iSBN10Regex = regexp.MustCompile(iSBN10RegexString)
iSBN13Regex = regexp.MustCompile(iSBN13RegexString)
iSSNRegex = regexp.MustCompile(iSSNRegexString)
uUID3Regex = regexp.MustCompile(uUID3RegexString)
uUID4Regex = regexp.MustCompile(uUID4RegexString)
uUID5Regex = regexp.MustCompile(uUID5RegexString)
uUIDRegex = regexp.MustCompile(uUIDRegexString)
uUID3RFC4122Regex = regexp.MustCompile(uUID3RFC4122RegexString)
uUID4RFC4122Regex = regexp.MustCompile(uUID4RFC4122RegexString)
uUID5RFC4122Regex = regexp.MustCompile(uUID5RFC4122RegexString)
uUIDRFC4122Regex = regexp.MustCompile(uUIDRFC4122RegexString)
uLIDRegex = regexp.MustCompile(uLIDRegexString)
md4Regex = regexp.MustCompile(md4RegexString)
md5Regex = regexp.MustCompile(md5RegexString)
sha256Regex = regexp.MustCompile(sha256RegexString)
sha384Regex = regexp.MustCompile(sha384RegexString)
sha512Regex = regexp.MustCompile(sha512RegexString)
ripemd128Regex = regexp.MustCompile(ripemd128RegexString)
ripemd160Regex = regexp.MustCompile(ripemd160RegexString)
tiger128Regex = regexp.MustCompile(tiger128RegexString)
tiger160Regex = regexp.MustCompile(tiger160RegexString)
tiger192Regex = regexp.MustCompile(tiger192RegexString)
aSCIIRegex = regexp.MustCompile(aSCIIRegexString)
printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString)
multibyteRegex = regexp.MustCompile(multibyteRegexString)
dataURIRegex = regexp.MustCompile(dataURIRegexString)
latitudeRegex = regexp.MustCompile(latitudeRegexString)
longitudeRegex = regexp.MustCompile(longitudeRegexString)
sSNRegex = regexp.MustCompile(sSNRegexString)
hostnameRegexRFC952 = regexp.MustCompile(hostnameRegexStringRFC952)
hostnameRegexRFC1123 = regexp.MustCompile(hostnameRegexStringRFC1123)
fqdnRegexRFC1123 = regexp.MustCompile(fqdnRegexStringRFC1123)
btcAddressRegex = regexp.MustCompile(btcAddressRegexString)
btcUpperAddressRegexBech32 = regexp.MustCompile(btcAddressUpperRegexStringBech32)
btcLowerAddressRegexBech32 = regexp.MustCompile(btcAddressLowerRegexStringBech32)
ethAddressRegex = regexp.MustCompile(ethAddressRegexString)
uRLEncodedRegex = regexp.MustCompile(uRLEncodedRegexString)
hTMLEncodedRegex = regexp.MustCompile(hTMLEncodedRegexString)
hTMLRegex = regexp.MustCompile(hTMLRegexString)
jWTRegex = regexp.MustCompile(jWTRegexString)
splitParamsRegex = regexp.MustCompile(splitParamsRegexString)
bicRegex = regexp.MustCompile(bicRegexString)
semverRegex = regexp.MustCompile(semverRegexString)
dnsRegexRFC1035Label = regexp.MustCompile(dnsRegexStringRFC1035Label)
cveRegex = regexp.MustCompile(cveRegexString)
mongodbRegex = regexp.MustCompile(mongodbRegexString)
cronRegex = regexp.MustCompile(cronRegexString)
spicedbIDRegex = regexp.MustCompile(spicedbIDRegexString)
spicedbPermissionRegex = regexp.MustCompile(spicedbPermissionRegexString)
spicedbTypeRegex = regexp.MustCompile(spicedbTypeRegexString)
)

View File

@@ -46,9 +46,9 @@ type StructLevel interface {
//
// NOTES:
//
// fieldName and structFieldName get appended to the existing
// namespace that validator is on. e.g. pass 'FirstName' or
// 'Names[0]' depending on the nesting
// fieldName and altName get appended to the existing namespace that
// validator is on. e.g. pass 'FirstName' or 'Names[0]' depending
// on the nesting
//
// tag can be an existing validation tag or just something you make up
// and process on the flip side it's up to you.
@@ -107,6 +107,7 @@ func (v *validate) ExtractType(field reflect.Value) (reflect.Value, reflect.Kind
// ReportError reports an error just by passing the field and tag information
func (v *validate) ReportError(field interface{}, fieldName, structFieldName, tag, param string) {
fv, kind, _ := v.extractTypeInternal(reflect.ValueOf(field), false)
if len(structFieldName) == 0 {
@@ -122,6 +123,7 @@ func (v *validate) ReportError(field interface{}, fieldName, structFieldName, ta
}
if kind == reflect.Invalid {
v.errs = append(v.errs,
&fieldError{
v: v.v,
@@ -147,7 +149,7 @@ func (v *validate) ReportError(field interface{}, fieldName, structFieldName, ta
structNs: v.str2,
fieldLen: uint8(len(fieldName)),
structfieldLen: uint8(len(structFieldName)),
value: getValue(fv),
value: fv.Interface(),
param: param,
kind: kind,
typ: fv.Type(),
@@ -159,9 +161,11 @@ func (v *validate) ReportError(field interface{}, fieldName, structFieldName, ta
//
// NOTE: this function prepends the current namespace to the relative ones.
func (v *validate) ReportValidationErrors(relativeNamespace, relativeStructNamespace string, errs ValidationErrors) {
var err *fieldError
for i := 0; i < len(errs); i++ {
err = errs[i].(*fieldError)
err.ns = string(append(append(v.ns, relativeNamespace...), err.ns...))
err.structNs = string(append(append(v.actualNs, relativeStructNamespace...), err.structNs...))

View File

@@ -13,6 +13,7 @@ import (
// It will dive into pointers, customTypes and return you the
// underlying value and it's kind.
func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (reflect.Value, reflect.Kind, bool) {
BEGIN:
switch current.Kind() {
case reflect.Ptr:
@@ -43,6 +44,7 @@ BEGIN:
default:
if v.v.hasCustomFuncs {
if fn, ok := v.v.customFuncs[current.Type()]; ok {
current = reflect.ValueOf(fn(current))
goto BEGIN
@@ -59,6 +61,7 @@ BEGIN:
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
// could not be retrieved because it didn't exist.
func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, nullable bool, found bool) {
BEGIN:
current, kind, nullable = v.ExtractType(val)
if kind == reflect.Invalid {
@@ -71,6 +74,7 @@ BEGIN:
}
switch kind {
case reflect.Ptr, reflect.Interface:
return
@@ -81,6 +85,7 @@ BEGIN:
var ns string
if !typ.ConvertibleTo(timeType) {
idx := strings.Index(namespace, namespaceSeparator)
if idx != -1 {
@@ -217,7 +222,7 @@ BEGIN:
panic("Invalid field namespace")
}
// asInt returns the parameter as an int64
// asInt returns the parameter as a int64
// or panics if it can't convert
func asInt(param string) int64 {
i, err := strconv.ParseInt(param, 0, 64)
@@ -251,6 +256,7 @@ func asIntFromType(t reflect.Type, param string) int64 {
// asUint returns the parameter as a uint64
// or panics if it can't convert
func asUint(param string) uint64 {
i, err := strconv.ParseUint(param, 0, 64)
panicIf(err)
@@ -265,7 +271,7 @@ func asFloat64(param string) float64 {
return i
}
// asFloat32 returns the parameter as a float32
// asFloat64 returns the parameter as a float64
// or panics if it can't convert
func asFloat32(param string) float64 {
i, err := strconv.ParseFloat(param, 32)
@@ -276,6 +282,7 @@ func asFloat32(param string) float64 {
// asBool returns the parameter as a bool
// or panics if it can't convert
func asBool(param string) bool {
i, err := strconv.ParseBool(param)
panicIf(err)
@@ -290,13 +297,12 @@ func panicIf(err error) {
// Checks if field value matches regex. If fl.Field can be cast to Stringer, it uses the Stringer interfaces
// String() return value. Otherwise, it uses fl.Field's String() value.
func fieldMatchesRegexByStringerValOrString(regexFn func() *regexp.Regexp, fl FieldLevel) bool {
regex := regexFn()
func fieldMatchesRegexByStringerValOrString(regex *regexp.Regexp, fl FieldLevel) bool {
switch fl.Field().Kind() {
case reflect.String:
return regex.MatchString(fl.Field().String())
default:
if stringer, ok := getValue(fl.Field()).(fmt.Stringer); ok {
if stringer, ok := fl.Field().Interface().(fmt.Stringer); ok {
return regex.MatchString(stringer.String())
} else {
return regex.MatchString(fl.Field().String())

View File

@@ -32,12 +32,14 @@ type validate struct {
// parent and current will be the same the first run of validateStruct
func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {
cs, ok := v.v.structCache.Get(typ)
if !ok {
cs = v.v.extractStructCache(current, typ.Name())
}
if len(ns) == 0 && len(cs.name) != 0 {
ns = append(ns, cs.name...)
ns = append(ns, '.')
@@ -48,17 +50,21 @@ func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, cur
// ct is nil on top level struct, and structs as fields that have no tag info
// so if nil or if not nil and the structonly tag isn't present
if ct == nil || ct.typeof != typeStructOnly {
var f *cField
for i := 0; i < len(cs.fields); i++ {
f = cs.fields[i]
if v.isPartial {
if v.ffn != nil {
// used with StructFiltered
if v.ffn(append(structNs, f.name...)) {
continue
}
} else {
// used with StructPartial & StructExcept
_, ok = v.includeExclude[string(append(structNs, f.name...))]
@@ -77,6 +83,7 @@ func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, cur
// first iteration will have no info about nostructlevel tag, and is checked prior to
// calling the next iteration of validateStruct called from traverseField.
if cs.fn != nil {
v.slflParent = parent
v.slCurrent = current
v.ns = ns
@@ -110,10 +117,6 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
return
}
if ct.typeof == typeOmitZero {
return
}
if ct.hasTag {
if kind == reflect.Invalid {
v.str1 = string(append(ns, cf.altName...))
@@ -235,19 +238,6 @@ OUTER:
ct = ct.next
continue
case typeOmitZero:
v.slflParent = parent
v.flField = current
v.cf = cf
v.ct = ct
if !hasNotZeroValue(v) {
return
}
ct = ct.next
continue
case typeOmitNil:
v.slflParent = parent
v.flField = current
@@ -260,7 +250,7 @@ OUTER:
return
}
default:
if v.fldIsPointer && getValue(field) == nil {
if v.fldIsPointer && field.Interface() == nil {
return
}
}
@@ -284,6 +274,7 @@ OUTER:
reusableCF := &cField{}
for i := 0; i < current.Len(); i++ {
i64 = int64(i)
v.misc = append(v.misc[0:0], cf.name...)
@@ -296,6 +287,7 @@ OUTER:
if cf.namesEqual {
reusableCF.altName = reusableCF.name
} else {
v.misc = append(v.misc[0:0], cf.altName...)
v.misc = append(v.misc, '[')
v.misc = strconv.AppendInt(v.misc, i64, 10)
@@ -312,7 +304,8 @@ OUTER:
reusableCF := &cField{}
for _, key := range current.MapKeys() {
pv = fmt.Sprintf("%v", key)
pv = fmt.Sprintf("%v", key.Interface())
v.misc = append(v.misc[0:0], cf.name...)
v.misc = append(v.misc, '[')
@@ -337,18 +330,6 @@ OUTER:
// can be nil when just keys being validated
if ct.next != nil {
v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct.next)
} else {
// Struct fallback when map values are structs
val := current.MapIndex(key)
switch val.Kind() {
case reflect.Ptr:
if val.Elem().Kind() == reflect.Struct {
// Dive into the struct so its own tags run
v.traverseField(ctx, parent, val, ns, structNs, reusableCF, nil)
}
case reflect.Struct:
v.traverseField(ctx, parent, val, ns, structNs, reusableCF, nil)
}
}
} else {
v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
@@ -368,6 +349,7 @@ OUTER:
v.misc = v.misc[0:0]
for {
// set Field Level fields
v.slflParent = parent
v.flField = current
@@ -382,6 +364,7 @@ OUTER:
// drain rest of the 'or' values, then continue or leave
for {
ct = ct.next
if ct == nil {
@@ -418,6 +401,7 @@ OUTER:
}
if ct.hasAlias {
v.errs = append(v.errs,
&fieldError{
v: v.v,
@@ -433,7 +417,9 @@ OUTER:
typ: typ,
},
)
} else {
tVal := string(v.misc)[1:]
v.errs = append(v.errs,
@@ -497,6 +483,7 @@ OUTER:
ct = ct.next
}
}
}
func getValue(val reflect.Value) interface{} {

View File

@@ -21,7 +21,6 @@ const (
tagKeySeparator = "="
structOnlyTag = "structonly"
noStructLevelTag = "nostructlevel"
omitzero = "omitzero"
omitempty = "omitempty"
omitnil = "omitnil"
isdefault = "isdefault"
@@ -75,8 +74,8 @@ type CustomTypeFunc func(field reflect.Value) interface{}
type TagNameFunc func(field reflect.StructField) string
type internalValidationFuncWrapper struct {
fn FuncCtx
runValidationOnNil bool
fn FuncCtx
runValidatinOnNil bool
}
// Validate contains the validator settings and cache
@@ -104,6 +103,7 @@ type Validate struct {
// in essence only parsing your validation tags once per struct type.
// Using multiple instances neglects the benefit of caching.
func New(options ...Option) *Validate {
tc := new(tagCache)
tc.m.Store(make(map[string]*cTag))
@@ -125,6 +125,7 @@ func New(options ...Option) *Validate {
// must copy validators for separate validations to be used in each instance
for k, val := range bakedInValidators {
switch k {
// these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour
case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag,
@@ -231,12 +232,30 @@ func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx, callValidationE
return v.registerValidation(tag, fn, false, nilCheckable)
}
func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error {
if len(tag) == 0 {
return errors.New("function Key cannot be empty")
}
if fn == nil {
return errors.New("function cannot be empty")
}
_, ok := restrictedTags[tag]
if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
panic(fmt.Sprintf(restrictedTagErr, tag))
}
v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidatinOnNil: nilCheckable}
return nil
}
// RegisterAlias registers a mapping of a single validation tag that
// defines a common or complex set of validation(s) to simplify adding validation
// to structs.
//
// NOTE: this function is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterAlias(alias, tags string) {
_, ok := restrictedTags[alias]
if ok || strings.ContainsAny(alias, restrictedTagChars) {
@@ -260,6 +279,7 @@ func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interfa
// NOTE:
// - this method is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...interface{}) {
if v.structLevelFuncs == nil {
v.structLevelFuncs = make(map[reflect.Type]StructLevelFuncCtx)
}
@@ -306,6 +326,7 @@ func (v *Validate) RegisterStructValidationMapRules(rules map[string]string, typ
//
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) {
if v.customFuncs == nil {
v.customFuncs = make(map[reflect.Type]CustomTypeFunc)
}
@@ -319,6 +340,7 @@ func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{
// RegisterTranslation registers translations against the provided tag.
func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) {
if v.transTagFunc == nil {
v.transTagFunc = make(map[ut.Translator]map[string]TranslationFunc)
}
@@ -352,6 +374,7 @@ func (v *Validate) Struct(s interface{}) error {
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) {
val := reflect.ValueOf(s)
top := val
@@ -468,8 +491,10 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields .
name := typ.Name()
for _, k := range fields {
flds := strings.Split(k, namespaceSeparator)
if len(flds) > 0 {
vd.misc = append(vd.misc[0:0], name...)
// Don't append empty name for unnamed structs
if len(vd.misc) != 0 {
@@ -477,6 +502,7 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields .
}
for _, s := range flds {
idx := strings.Index(s, leftBracket)
if idx != -1 {
@@ -492,6 +518,7 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields .
idx = strings.Index(s, leftBracket)
}
} else {
vd.misc = append(vd.misc, s...)
vd.includeExclude[string(vd.misc)] = struct{}{}
}
@@ -554,6 +581,7 @@ func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields ..
name := typ.Name()
for _, key := range fields {
vd.misc = vd.misc[0:0]
if len(name) > 0 {
@@ -648,7 +676,7 @@ func (v *Validate) VarWithValue(field interface{}, other interface{}, tag string
}
// VarWithValueCtx validates a single variable, against another variable/field's value using tag style validation and
// allows passing of contextual validation information via context.Context.
// allows passing of contextual validation validation information via context.Context.
// eg.
// s1 := "abcd"
// s2 := "abcd"
@@ -680,20 +708,3 @@ func (v *Validate) VarWithValueCtx(ctx context.Context, field interface{}, other
v.pool.Put(vd)
return
}
func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error {
if len(tag) == 0 {
return errors.New("function Key cannot be empty")
}
if fn == nil {
return errors.New("function cannot be empty")
}
_, ok := restrictedTags[tag]
if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
panic(fmt.Sprintf(restrictedTagErr, tag))
}
v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidationOnNil: nilCheckable}
return nil
}