mirror of
https://github.com/astaxie/beego.git
synced 2025-06-12 07:40:39 +00:00
Merge pull request #4173 from AllenX2018/fix-bug-queryRow
Fix issue 3866
This commit is contained in:
147
pkg/infrastructure/validation/README.md
Normal file
147
pkg/infrastructure/validation/README.md
Normal file
@ -0,0 +1,147 @@
|
||||
validation
|
||||
==============
|
||||
|
||||
validation is a form validation for a data validation and error collecting using Go.
|
||||
|
||||
## Installation and tests
|
||||
|
||||
Install:
|
||||
|
||||
go get github.com/astaxie/beego/validation
|
||||
|
||||
Test:
|
||||
|
||||
go test github.com/astaxie/beego/validation
|
||||
|
||||
## Example
|
||||
|
||||
Direct Use:
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego/validation"
|
||||
"log"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
func main() {
|
||||
u := User{"man", 40}
|
||||
valid := validation.Validation{}
|
||||
valid.Required(u.Name, "name")
|
||||
valid.MaxSize(u.Name, 15, "nameMax")
|
||||
valid.Range(u.Age, 0, 140, "age")
|
||||
if valid.HasErrors() {
|
||||
// validation does not pass
|
||||
// print invalid message
|
||||
for _, err := range valid.Errors {
|
||||
log.Println(err.Key, err.Message)
|
||||
}
|
||||
}
|
||||
// or use like this
|
||||
if v := valid.Max(u.Age, 140, "ageMax"); !v.Ok {
|
||||
log.Println(v.Error.Key, v.Error.Message)
|
||||
}
|
||||
}
|
||||
|
||||
Struct Tag Use:
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego/validation"
|
||||
)
|
||||
|
||||
// validation function follow with "valid" tag
|
||||
// functions divide with ";"
|
||||
// parameters in parentheses "()" and divide with ","
|
||||
// Match function's pattern string must in "//"
|
||||
type user struct {
|
||||
Id int
|
||||
Name string `valid:"Required;Match(/^(test)?\\w*@;com$/)"`
|
||||
Age int `valid:"Required;Range(1, 140)"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
valid := validation.Validation{}
|
||||
// ignore empty field valid
|
||||
// see CanSkipFuncs
|
||||
// valid := validation.Validation{RequiredFirst:true}
|
||||
u := user{Name: "test", Age: 40}
|
||||
b, err := valid.Valid(u)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
if !b {
|
||||
// validation does not pass
|
||||
// blabla...
|
||||
}
|
||||
}
|
||||
|
||||
Use custom function:
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego/validation"
|
||||
)
|
||||
|
||||
type user struct {
|
||||
Id int
|
||||
Name string `valid:"Required;IsMe"`
|
||||
Age int `valid:"Required;Range(1, 140)"`
|
||||
}
|
||||
|
||||
func IsMe(v *validation.Validation, obj interface{}, key string) {
|
||||
name, ok:= obj.(string)
|
||||
if !ok {
|
||||
// wrong use case?
|
||||
return
|
||||
}
|
||||
|
||||
if name != "me" {
|
||||
// valid false
|
||||
v.SetError("Name", "is not me!")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
valid := validation.Validation{}
|
||||
if err := validation.AddCustomFunc("IsMe", IsMe); err != nil {
|
||||
// hadle error
|
||||
}
|
||||
u := user{Name: "test", Age: 40}
|
||||
b, err := valid.Valid(u)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
if !b {
|
||||
// validation does not pass
|
||||
// blabla...
|
||||
}
|
||||
}
|
||||
|
||||
Struct Tag Functions:
|
||||
|
||||
Required
|
||||
Min(min int)
|
||||
Max(max int)
|
||||
Range(min, max int)
|
||||
MinSize(min int)
|
||||
MaxSize(max int)
|
||||
Length(length int)
|
||||
Alpha
|
||||
Numeric
|
||||
AlphaNumeric
|
||||
Match(pattern string)
|
||||
AlphaDash
|
||||
Email
|
||||
IP
|
||||
Base64
|
||||
Mobile
|
||||
Tel
|
||||
Phone
|
||||
ZipCode
|
||||
|
||||
|
||||
## LICENSE
|
||||
|
||||
BSD License http://creativecommons.org/licenses/BSD/
|
298
pkg/infrastructure/validation/util.go
Normal file
298
pkg/infrastructure/validation/util.go
Normal file
@ -0,0 +1,298 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// ValidTag struct tag
|
||||
ValidTag = "valid"
|
||||
|
||||
LabelTag = "label"
|
||||
|
||||
wordsize = 32 << (^uint(0) >> 32 & 1)
|
||||
)
|
||||
|
||||
var (
|
||||
// key: function name
|
||||
// value: the number of parameters
|
||||
funcs = make(Funcs)
|
||||
|
||||
// doesn't belong to validation functions
|
||||
unFuncs = map[string]bool{
|
||||
"Clear": true,
|
||||
"HasErrors": true,
|
||||
"ErrorMap": true,
|
||||
"Error": true,
|
||||
"apply": true,
|
||||
"Check": true,
|
||||
"Valid": true,
|
||||
"NoMatch": true,
|
||||
}
|
||||
// ErrInt64On32 show 32 bit platform not support int64
|
||||
ErrInt64On32 = fmt.Errorf("not support int64 on 32-bit platform")
|
||||
)
|
||||
|
||||
func init() {
|
||||
v := &Validation{}
|
||||
t := reflect.TypeOf(v)
|
||||
for i := 0; i < t.NumMethod(); i++ {
|
||||
m := t.Method(i)
|
||||
if !unFuncs[m.Name] {
|
||||
funcs[m.Name] = m.Func
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CustomFunc is for custom validate function
|
||||
type CustomFunc func(v *Validation, obj interface{}, key string)
|
||||
|
||||
// AddCustomFunc Add a custom function to validation
|
||||
// The name can not be:
|
||||
// Clear
|
||||
// HasErrors
|
||||
// ErrorMap
|
||||
// Error
|
||||
// Check
|
||||
// Valid
|
||||
// NoMatch
|
||||
// If the name is same with exists function, it will replace the origin valid function
|
||||
func AddCustomFunc(name string, f CustomFunc) error {
|
||||
if unFuncs[name] {
|
||||
return fmt.Errorf("invalid function name: %s", name)
|
||||
}
|
||||
|
||||
funcs[name] = reflect.ValueOf(f)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidFunc Valid function type
|
||||
type ValidFunc struct {
|
||||
Name string
|
||||
Params []interface{}
|
||||
}
|
||||
|
||||
// Funcs Validate function map
|
||||
type Funcs map[string]reflect.Value
|
||||
|
||||
// Call validate values with named type string
|
||||
func (f Funcs) Call(name string, params ...interface{}) (result []reflect.Value, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("%v", r)
|
||||
}
|
||||
}()
|
||||
if _, ok := f[name]; !ok {
|
||||
err = fmt.Errorf("%s does not exist", name)
|
||||
return
|
||||
}
|
||||
if len(params) != f[name].Type().NumIn() {
|
||||
err = fmt.Errorf("The number of params is not adapted")
|
||||
return
|
||||
}
|
||||
in := make([]reflect.Value, len(params))
|
||||
for k, param := range params {
|
||||
in[k] = reflect.ValueOf(param)
|
||||
}
|
||||
result = f[name].Call(in)
|
||||
return
|
||||
}
|
||||
|
||||
func isStruct(t reflect.Type) bool {
|
||||
return t.Kind() == reflect.Struct
|
||||
}
|
||||
|
||||
func isStructPtr(t reflect.Type) bool {
|
||||
return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
|
||||
}
|
||||
|
||||
func getValidFuncs(f reflect.StructField) (vfs []ValidFunc, err error) {
|
||||
tag := f.Tag.Get(ValidTag)
|
||||
label := f.Tag.Get(LabelTag)
|
||||
if len(tag) == 0 {
|
||||
return
|
||||
}
|
||||
if vfs, tag, err = getRegFuncs(tag, f.Name); err != nil {
|
||||
return
|
||||
}
|
||||
fs := strings.Split(tag, ";")
|
||||
for _, vfunc := range fs {
|
||||
var vf ValidFunc
|
||||
if len(vfunc) == 0 {
|
||||
continue
|
||||
}
|
||||
vf, err = parseFunc(vfunc, f.Name, label)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
vfs = append(vfs, vf)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get Match function
|
||||
// May be get NoMatch function in the future
|
||||
func getRegFuncs(tag, key string) (vfs []ValidFunc, str string, err error) {
|
||||
tag = strings.TrimSpace(tag)
|
||||
index := strings.Index(tag, "Match(/")
|
||||
if index == -1 {
|
||||
str = tag
|
||||
return
|
||||
}
|
||||
end := strings.LastIndex(tag, "/)")
|
||||
if end < index {
|
||||
err = fmt.Errorf("invalid Match function")
|
||||
return
|
||||
}
|
||||
reg, err := regexp.Compile(tag[index+len("Match(/") : end])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
vfs = []ValidFunc{{"Match", []interface{}{reg, key + ".Match"}}}
|
||||
str = strings.TrimSpace(tag[:index]) + strings.TrimSpace(tag[end+len("/)"):])
|
||||
return
|
||||
}
|
||||
|
||||
func parseFunc(vfunc, key string, label string) (v ValidFunc, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("%v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
vfunc = strings.TrimSpace(vfunc)
|
||||
start := strings.Index(vfunc, "(")
|
||||
var num int
|
||||
|
||||
// doesn't need parameter valid function
|
||||
if start == -1 {
|
||||
if num, err = numIn(vfunc); err != nil {
|
||||
return
|
||||
}
|
||||
if num != 0 {
|
||||
err = fmt.Errorf("%s require %d parameters", vfunc, num)
|
||||
return
|
||||
}
|
||||
v = ValidFunc{vfunc, []interface{}{key + "." + vfunc + "." + label}}
|
||||
return
|
||||
}
|
||||
|
||||
end := strings.Index(vfunc, ")")
|
||||
if end == -1 {
|
||||
err = fmt.Errorf("invalid valid function")
|
||||
return
|
||||
}
|
||||
|
||||
name := strings.TrimSpace(vfunc[:start])
|
||||
if num, err = numIn(name); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params := strings.Split(vfunc[start+1:end], ",")
|
||||
// the num of param must be equal
|
||||
if num != len(params) {
|
||||
err = fmt.Errorf("%s require %d parameters", name, num)
|
||||
return
|
||||
}
|
||||
|
||||
tParams, err := trim(name, key+"."+name+"."+label, params)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
v = ValidFunc{name, tParams}
|
||||
return
|
||||
}
|
||||
|
||||
func numIn(name string) (num int, err error) {
|
||||
fn, ok := funcs[name]
|
||||
if !ok {
|
||||
err = fmt.Errorf("doesn't exists %s valid function", name)
|
||||
return
|
||||
}
|
||||
// sub *Validation obj and key
|
||||
num = fn.Type().NumIn() - 3
|
||||
return
|
||||
}
|
||||
|
||||
func trim(name, key string, s []string) (ts []interface{}, err error) {
|
||||
ts = make([]interface{}, len(s), len(s)+1)
|
||||
fn, ok := funcs[name]
|
||||
if !ok {
|
||||
err = fmt.Errorf("doesn't exists %s valid function", name)
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(s); i++ {
|
||||
var param interface{}
|
||||
// skip *Validation and obj params
|
||||
if param, err = parseParam(fn.Type().In(i+2), strings.TrimSpace(s[i])); err != nil {
|
||||
return
|
||||
}
|
||||
ts[i] = param
|
||||
}
|
||||
ts = append(ts, key)
|
||||
return
|
||||
}
|
||||
|
||||
// modify the parameters's type to adapt the function input parameters' type
|
||||
func parseParam(t reflect.Type, s string) (i interface{}, err error) {
|
||||
switch t.Kind() {
|
||||
case reflect.Int:
|
||||
i, err = strconv.Atoi(s)
|
||||
case reflect.Int64:
|
||||
if wordsize == 32 {
|
||||
return nil, ErrInt64On32
|
||||
}
|
||||
i, err = strconv.ParseInt(s, 10, 64)
|
||||
case reflect.Int32:
|
||||
var v int64
|
||||
v, err = strconv.ParseInt(s, 10, 32)
|
||||
if err == nil {
|
||||
i = int32(v)
|
||||
}
|
||||
case reflect.Int16:
|
||||
var v int64
|
||||
v, err = strconv.ParseInt(s, 10, 16)
|
||||
if err == nil {
|
||||
i = int16(v)
|
||||
}
|
||||
case reflect.Int8:
|
||||
var v int64
|
||||
v, err = strconv.ParseInt(s, 10, 8)
|
||||
if err == nil {
|
||||
i = int8(v)
|
||||
}
|
||||
case reflect.String:
|
||||
i = s
|
||||
case reflect.Ptr:
|
||||
if t.Elem().String() != "regexp.Regexp" {
|
||||
err = fmt.Errorf("not support %s", t.Elem().String())
|
||||
return
|
||||
}
|
||||
i, err = regexp.Compile(s)
|
||||
default:
|
||||
err = fmt.Errorf("not support %s", t.Kind().String())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func mergeParam(v *Validation, obj interface{}, params []interface{}) []interface{} {
|
||||
return append([]interface{}{v, obj}, params...)
|
||||
}
|
128
pkg/infrastructure/validation/util_test.go
Normal file
128
pkg/infrastructure/validation/util_test.go
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validation
|
||||
|
||||
import (
|
||||
"log"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type user struct {
|
||||
ID int
|
||||
Tag string `valid:"Maxx(aa)"`
|
||||
Name string `valid:"Required;"`
|
||||
Age int `valid:"Required; Range(1, 140)"`
|
||||
match string `valid:"Required; Match(/^(test)?\\w*@(/test/);com$/);Max(2)"`
|
||||
}
|
||||
|
||||
func TestGetValidFuncs(t *testing.T) {
|
||||
u := user{Name: "test", Age: 1}
|
||||
tf := reflect.TypeOf(u)
|
||||
var vfs []ValidFunc
|
||||
var err error
|
||||
|
||||
f, _ := tf.FieldByName("ID")
|
||||
if vfs, err = getValidFuncs(f); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(vfs) != 0 {
|
||||
t.Fatal("should get none ValidFunc")
|
||||
}
|
||||
|
||||
f, _ = tf.FieldByName("Tag")
|
||||
if _, err = getValidFuncs(f); err.Error() != "doesn't exists Maxx valid function" {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
f, _ = tf.FieldByName("Name")
|
||||
if vfs, err = getValidFuncs(f); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(vfs) != 1 {
|
||||
t.Fatal("should get 1 ValidFunc")
|
||||
}
|
||||
if vfs[0].Name != "Required" && len(vfs[0].Params) != 0 {
|
||||
t.Error("Required funcs should be got")
|
||||
}
|
||||
|
||||
f, _ = tf.FieldByName("Age")
|
||||
if vfs, err = getValidFuncs(f); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(vfs) != 2 {
|
||||
t.Fatal("should get 2 ValidFunc")
|
||||
}
|
||||
if vfs[0].Name != "Required" && len(vfs[0].Params) != 0 {
|
||||
t.Error("Required funcs should be got")
|
||||
}
|
||||
if vfs[1].Name != "Range" && len(vfs[1].Params) != 2 {
|
||||
t.Error("Range funcs should be got")
|
||||
}
|
||||
|
||||
f, _ = tf.FieldByName("match")
|
||||
if vfs, err = getValidFuncs(f); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(vfs) != 3 {
|
||||
t.Fatal("should get 3 ValidFunc but now is", len(vfs))
|
||||
}
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string `valid:"Required;MaxSize(5)" `
|
||||
Sex string `valid:"Required;" label:"sex_label"`
|
||||
Age int `valid:"Required;Range(1, 140);" label:"age_label"`
|
||||
}
|
||||
|
||||
func TestValidation(t *testing.T) {
|
||||
u := User{"man1238888456", "", 1140}
|
||||
valid := Validation{}
|
||||
b, err := valid.Valid(&u)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
if !b {
|
||||
// validation does not pass
|
||||
// blabla...
|
||||
for _, err := range valid.Errors {
|
||||
log.Println(err.Key, err.Message)
|
||||
}
|
||||
if len(valid.Errors) != 3 {
|
||||
t.Error("must be has 3 error")
|
||||
}
|
||||
} else {
|
||||
t.Error("must be has 3 error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCall(t *testing.T) {
|
||||
u := user{Name: "test", Age: 180}
|
||||
tf := reflect.TypeOf(u)
|
||||
var vfs []ValidFunc
|
||||
var err error
|
||||
f, _ := tf.FieldByName("Age")
|
||||
if vfs, err = getValidFuncs(f); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
valid := &Validation{}
|
||||
vfs[1].Params = append([]interface{}{valid, u.Age}, vfs[1].Params...)
|
||||
if _, err = funcs.Call(vfs[1].Name, vfs[1].Params...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(valid.Errors) != 1 {
|
||||
t.Error("age out of range should be has an error")
|
||||
}
|
||||
}
|
456
pkg/infrastructure/validation/validation.go
Normal file
456
pkg/infrastructure/validation/validation.go
Normal file
@ -0,0 +1,456 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package validation for validations
|
||||
//
|
||||
// import (
|
||||
// "github.com/astaxie/beego/validation"
|
||||
// "log"
|
||||
// )
|
||||
//
|
||||
// type User struct {
|
||||
// Name string
|
||||
// Age int
|
||||
// }
|
||||
//
|
||||
// func main() {
|
||||
// u := User{"man", 40}
|
||||
// valid := validation.Validation{}
|
||||
// valid.Required(u.Name, "name")
|
||||
// valid.MaxSize(u.Name, 15, "nameMax")
|
||||
// valid.Range(u.Age, 0, 140, "age")
|
||||
// if valid.HasErrors() {
|
||||
// // validation does not pass
|
||||
// // print invalid message
|
||||
// for _, err := range valid.Errors {
|
||||
// log.Println(err.Key, err.Message)
|
||||
// }
|
||||
// }
|
||||
// // or use like this
|
||||
// if v := valid.Max(u.Age, 140, "ageMax"); !v.Ok {
|
||||
// log.Println(v.Error.Key, v.Error.Message)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// more info: http://beego.me/docs/mvc/controller/validation.md
|
||||
package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ValidFormer valid interface
|
||||
type ValidFormer interface {
|
||||
Valid(*Validation)
|
||||
}
|
||||
|
||||
// Error show the error
|
||||
type Error struct {
|
||||
Message, Key, Name, Field, Tmpl string
|
||||
Value interface{}
|
||||
LimitValue interface{}
|
||||
}
|
||||
|
||||
// String Returns the Message.
|
||||
func (e *Error) String() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
return e.Message
|
||||
}
|
||||
|
||||
// Implement Error interface.
|
||||
// Return e.String()
|
||||
func (e *Error) Error() string { return e.String() }
|
||||
|
||||
// Result is returned from every validation method.
|
||||
// It provides an indication of success, and a pointer to the Error (if any).
|
||||
type Result struct {
|
||||
Error *Error
|
||||
Ok bool
|
||||
}
|
||||
|
||||
// Key Get Result by given key string.
|
||||
func (r *Result) Key(key string) *Result {
|
||||
if r.Error != nil {
|
||||
r.Error.Key = key
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Message Set Result message by string or format string with args
|
||||
func (r *Result) Message(message string, args ...interface{}) *Result {
|
||||
if r.Error != nil {
|
||||
if len(args) == 0 {
|
||||
r.Error.Message = message
|
||||
} else {
|
||||
r.Error.Message = fmt.Sprintf(message, args...)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// A Validation context manages data validation and error messages.
|
||||
type Validation struct {
|
||||
// if this field set true, in struct tag valid
|
||||
// if the struct field vale is empty
|
||||
// it will skip those valid functions, see CanSkipFuncs
|
||||
RequiredFirst bool
|
||||
|
||||
Errors []*Error
|
||||
ErrorsMap map[string][]*Error
|
||||
}
|
||||
|
||||
// Clear Clean all ValidationError.
|
||||
func (v *Validation) Clear() {
|
||||
v.Errors = []*Error{}
|
||||
v.ErrorsMap = nil
|
||||
}
|
||||
|
||||
// HasErrors Has ValidationError nor not.
|
||||
func (v *Validation) HasErrors() bool {
|
||||
return len(v.Errors) > 0
|
||||
}
|
||||
|
||||
// ErrorMap Return the errors mapped by key.
|
||||
// If there are multiple validation errors associated with a single key, the
|
||||
// first one "wins". (Typically the first validation will be the more basic).
|
||||
func (v *Validation) ErrorMap() map[string][]*Error {
|
||||
return v.ErrorsMap
|
||||
}
|
||||
|
||||
// Error Add an error to the validation context.
|
||||
func (v *Validation) Error(message string, args ...interface{}) *Result {
|
||||
result := (&Result{
|
||||
Ok: false,
|
||||
Error: &Error{},
|
||||
}).Message(message, args...)
|
||||
v.Errors = append(v.Errors, result.Error)
|
||||
return result
|
||||
}
|
||||
|
||||
// Required Test that the argument is non-nil and non-empty (if string or list)
|
||||
func (v *Validation) Required(obj interface{}, key string) *Result {
|
||||
return v.apply(Required{key}, obj)
|
||||
}
|
||||
|
||||
// Min Test that the obj is greater than min if obj's type is int
|
||||
func (v *Validation) Min(obj interface{}, min int, key string) *Result {
|
||||
return v.apply(Min{min, key}, obj)
|
||||
}
|
||||
|
||||
// Max Test that the obj is less than max if obj's type is int
|
||||
func (v *Validation) Max(obj interface{}, max int, key string) *Result {
|
||||
return v.apply(Max{max, key}, obj)
|
||||
}
|
||||
|
||||
// Range Test that the obj is between mni and max if obj's type is int
|
||||
func (v *Validation) Range(obj interface{}, min, max int, key string) *Result {
|
||||
return v.apply(Range{Min{Min: min}, Max{Max: max}, key}, obj)
|
||||
}
|
||||
|
||||
// MinSize Test that the obj is longer than min size if type is string or slice
|
||||
func (v *Validation) MinSize(obj interface{}, min int, key string) *Result {
|
||||
return v.apply(MinSize{min, key}, obj)
|
||||
}
|
||||
|
||||
// MaxSize Test that the obj is shorter than max size if type is string or slice
|
||||
func (v *Validation) MaxSize(obj interface{}, max int, key string) *Result {
|
||||
return v.apply(MaxSize{max, key}, obj)
|
||||
}
|
||||
|
||||
// Length Test that the obj is same length to n if type is string or slice
|
||||
func (v *Validation) Length(obj interface{}, n int, key string) *Result {
|
||||
return v.apply(Length{n, key}, obj)
|
||||
}
|
||||
|
||||
// Alpha Test that the obj is [a-zA-Z] if type is string
|
||||
func (v *Validation) Alpha(obj interface{}, key string) *Result {
|
||||
return v.apply(Alpha{key}, obj)
|
||||
}
|
||||
|
||||
// Numeric Test that the obj is [0-9] if type is string
|
||||
func (v *Validation) Numeric(obj interface{}, key string) *Result {
|
||||
return v.apply(Numeric{key}, obj)
|
||||
}
|
||||
|
||||
// AlphaNumeric Test that the obj is [0-9a-zA-Z] if type is string
|
||||
func (v *Validation) AlphaNumeric(obj interface{}, key string) *Result {
|
||||
return v.apply(AlphaNumeric{key}, obj)
|
||||
}
|
||||
|
||||
// Match Test that the obj matches regexp if type is string
|
||||
func (v *Validation) Match(obj interface{}, regex *regexp.Regexp, key string) *Result {
|
||||
return v.apply(Match{regex, key}, obj)
|
||||
}
|
||||
|
||||
// NoMatch Test that the obj doesn't match regexp if type is string
|
||||
func (v *Validation) NoMatch(obj interface{}, regex *regexp.Regexp, key string) *Result {
|
||||
return v.apply(NoMatch{Match{Regexp: regex}, key}, obj)
|
||||
}
|
||||
|
||||
// AlphaDash Test that the obj is [0-9a-zA-Z_-] if type is string
|
||||
func (v *Validation) AlphaDash(obj interface{}, key string) *Result {
|
||||
return v.apply(AlphaDash{NoMatch{Match: Match{Regexp: alphaDashPattern}}, key}, obj)
|
||||
}
|
||||
|
||||
// Email Test that the obj is email address if type is string
|
||||
func (v *Validation) Email(obj interface{}, key string) *Result {
|
||||
return v.apply(Email{Match{Regexp: emailPattern}, key}, obj)
|
||||
}
|
||||
|
||||
// IP Test that the obj is IP address if type is string
|
||||
func (v *Validation) IP(obj interface{}, key string) *Result {
|
||||
return v.apply(IP{Match{Regexp: ipPattern}, key}, obj)
|
||||
}
|
||||
|
||||
// Base64 Test that the obj is base64 encoded if type is string
|
||||
func (v *Validation) Base64(obj interface{}, key string) *Result {
|
||||
return v.apply(Base64{Match{Regexp: base64Pattern}, key}, obj)
|
||||
}
|
||||
|
||||
// Mobile Test that the obj is chinese mobile number if type is string
|
||||
func (v *Validation) Mobile(obj interface{}, key string) *Result {
|
||||
return v.apply(Mobile{Match{Regexp: mobilePattern}, key}, obj)
|
||||
}
|
||||
|
||||
// Tel Test that the obj is chinese telephone number if type is string
|
||||
func (v *Validation) Tel(obj interface{}, key string) *Result {
|
||||
return v.apply(Tel{Match{Regexp: telPattern}, key}, obj)
|
||||
}
|
||||
|
||||
// Phone Test that the obj is chinese mobile or telephone number if type is string
|
||||
func (v *Validation) Phone(obj interface{}, key string) *Result {
|
||||
return v.apply(Phone{Mobile{Match: Match{Regexp: mobilePattern}},
|
||||
Tel{Match: Match{Regexp: telPattern}}, key}, obj)
|
||||
}
|
||||
|
||||
// ZipCode Test that the obj is chinese zip code if type is string
|
||||
func (v *Validation) ZipCode(obj interface{}, key string) *Result {
|
||||
return v.apply(ZipCode{Match{Regexp: zipCodePattern}, key}, obj)
|
||||
}
|
||||
|
||||
func (v *Validation) apply(chk Validator, obj interface{}) *Result {
|
||||
if nil == obj {
|
||||
if chk.IsSatisfied(obj) {
|
||||
return &Result{Ok: true}
|
||||
}
|
||||
} else if reflect.TypeOf(obj).Kind() == reflect.Ptr {
|
||||
if reflect.ValueOf(obj).IsNil() {
|
||||
if chk.IsSatisfied(nil) {
|
||||
return &Result{Ok: true}
|
||||
}
|
||||
} else {
|
||||
if chk.IsSatisfied(reflect.ValueOf(obj).Elem().Interface()) {
|
||||
return &Result{Ok: true}
|
||||
}
|
||||
}
|
||||
} else if chk.IsSatisfied(obj) {
|
||||
return &Result{Ok: true}
|
||||
}
|
||||
|
||||
// Add the error to the validation context.
|
||||
key := chk.GetKey()
|
||||
Name := key
|
||||
Field := ""
|
||||
Label := ""
|
||||
parts := strings.Split(key, ".")
|
||||
if len(parts) == 3 {
|
||||
Field = parts[0]
|
||||
Name = parts[1]
|
||||
Label = parts[2]
|
||||
if len(Label) == 0 {
|
||||
Label = Field
|
||||
}
|
||||
}
|
||||
|
||||
err := &Error{
|
||||
Message: Label + " " + chk.DefaultMessage(),
|
||||
Key: key,
|
||||
Name: Name,
|
||||
Field: Field,
|
||||
Value: obj,
|
||||
Tmpl: MessageTmpls[Name],
|
||||
LimitValue: chk.GetLimitValue(),
|
||||
}
|
||||
v.setError(err)
|
||||
|
||||
// Also return it in the result.
|
||||
return &Result{
|
||||
Ok: false,
|
||||
Error: err,
|
||||
}
|
||||
}
|
||||
|
||||
// key must like aa.bb.cc or aa.bb.
|
||||
// AddError adds independent error message for the provided key
|
||||
func (v *Validation) AddError(key, message string) {
|
||||
Name := key
|
||||
Field := ""
|
||||
|
||||
Label := ""
|
||||
parts := strings.Split(key, ".")
|
||||
if len(parts) == 3 {
|
||||
Field = parts[0]
|
||||
Name = parts[1]
|
||||
Label = parts[2]
|
||||
if len(Label) == 0 {
|
||||
Label = Field
|
||||
}
|
||||
}
|
||||
|
||||
err := &Error{
|
||||
Message: Label + " " + message,
|
||||
Key: key,
|
||||
Name: Name,
|
||||
Field: Field,
|
||||
}
|
||||
v.setError(err)
|
||||
}
|
||||
|
||||
func (v *Validation) setError(err *Error) {
|
||||
v.Errors = append(v.Errors, err)
|
||||
if v.ErrorsMap == nil {
|
||||
v.ErrorsMap = make(map[string][]*Error)
|
||||
}
|
||||
if _, ok := v.ErrorsMap[err.Field]; !ok {
|
||||
v.ErrorsMap[err.Field] = []*Error{}
|
||||
}
|
||||
v.ErrorsMap[err.Field] = append(v.ErrorsMap[err.Field], err)
|
||||
}
|
||||
|
||||
// SetError Set error message for one field in ValidationError
|
||||
func (v *Validation) SetError(fieldName string, errMsg string) *Error {
|
||||
err := &Error{Key: fieldName, Field: fieldName, Tmpl: errMsg, Message: errMsg}
|
||||
v.setError(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Check Apply a group of validators to a field, in order, and return the
|
||||
// ValidationResult from the first one that fails, or the last one that
|
||||
// succeeds.
|
||||
func (v *Validation) Check(obj interface{}, checks ...Validator) *Result {
|
||||
var result *Result
|
||||
for _, check := range checks {
|
||||
result = v.apply(check, obj)
|
||||
if !result.Ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Valid Validate a struct.
|
||||
// the obj parameter must be a struct or a struct pointer
|
||||
func (v *Validation) Valid(obj interface{}) (b bool, err error) {
|
||||
objT := reflect.TypeOf(obj)
|
||||
objV := reflect.ValueOf(obj)
|
||||
switch {
|
||||
case isStruct(objT):
|
||||
case isStructPtr(objT):
|
||||
objT = objT.Elem()
|
||||
objV = objV.Elem()
|
||||
default:
|
||||
err = fmt.Errorf("%v must be a struct or a struct pointer", obj)
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < objT.NumField(); i++ {
|
||||
var vfs []ValidFunc
|
||||
if vfs, err = getValidFuncs(objT.Field(i)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var hasRequired bool
|
||||
for _, vf := range vfs {
|
||||
if vf.Name == "Required" {
|
||||
hasRequired = true
|
||||
}
|
||||
|
||||
currentField := objV.Field(i).Interface()
|
||||
if objV.Field(i).Kind() == reflect.Ptr {
|
||||
if objV.Field(i).IsNil() {
|
||||
currentField = ""
|
||||
} else {
|
||||
currentField = objV.Field(i).Elem().Interface()
|
||||
}
|
||||
}
|
||||
|
||||
chk := Required{""}.IsSatisfied(currentField)
|
||||
if !hasRequired && v.RequiredFirst && !chk {
|
||||
if _, ok := CanSkipFuncs[vf.Name]; ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if _, err = funcs.Call(vf.Name,
|
||||
mergeParam(v, objV.Field(i).Interface(), vf.Params)...); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !v.HasErrors() {
|
||||
if form, ok := obj.(ValidFormer); ok {
|
||||
form.Valid(v)
|
||||
}
|
||||
}
|
||||
|
||||
return !v.HasErrors(), nil
|
||||
}
|
||||
|
||||
// RecursiveValid Recursively validate a struct.
|
||||
// Step1: Validate by v.Valid
|
||||
// Step2: If pass on step1, then reflect obj's fields
|
||||
// Step3: Do the Recursively validation to all struct or struct pointer fields
|
||||
func (v *Validation) RecursiveValid(objc interface{}) (bool, error) {
|
||||
//Step 1: validate obj itself firstly
|
||||
// fails if objc is not struct
|
||||
pass, err := v.Valid(objc)
|
||||
if err != nil || !pass {
|
||||
return pass, err // Stop recursive validation
|
||||
}
|
||||
// Step 2: Validate struct's struct fields
|
||||
objT := reflect.TypeOf(objc)
|
||||
objV := reflect.ValueOf(objc)
|
||||
|
||||
if isStructPtr(objT) {
|
||||
objT = objT.Elem()
|
||||
objV = objV.Elem()
|
||||
}
|
||||
|
||||
for i := 0; i < objT.NumField(); i++ {
|
||||
|
||||
t := objT.Field(i).Type
|
||||
|
||||
// Recursive applies to struct or pointer to structs fields
|
||||
if isStruct(t) || isStructPtr(t) {
|
||||
// Step 3: do the recursive validation
|
||||
// Only valid the Public field recursively
|
||||
if objV.Field(i).CanInterface() {
|
||||
pass, err = v.RecursiveValid(objV.Field(i).Interface())
|
||||
}
|
||||
}
|
||||
}
|
||||
return pass, err
|
||||
}
|
||||
|
||||
func (v *Validation) CanSkipAlso(skipFunc string) {
|
||||
if _, ok := CanSkipFuncs[skipFunc]; !ok {
|
||||
CanSkipFuncs[skipFunc] = struct{}{}
|
||||
}
|
||||
}
|
609
pkg/infrastructure/validation/validation_test.go
Normal file
609
pkg/infrastructure/validation/validation_test.go
Normal file
@ -0,0 +1,609 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validation
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRequired(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Required(nil, "nil").Ok {
|
||||
t.Error("nil object should be false")
|
||||
}
|
||||
if !valid.Required(true, "bool").Ok {
|
||||
t.Error("Bool value should always return true")
|
||||
}
|
||||
if !valid.Required(false, "bool").Ok {
|
||||
t.Error("Bool value should always return true")
|
||||
}
|
||||
if valid.Required("", "string").Ok {
|
||||
t.Error("\"'\" string should be false")
|
||||
}
|
||||
if valid.Required(" ", "string").Ok {
|
||||
t.Error("\" \" string should be false") // For #2361
|
||||
}
|
||||
if valid.Required("\n", "string").Ok {
|
||||
t.Error("new line string should be false") // For #2361
|
||||
}
|
||||
if !valid.Required("astaxie", "string").Ok {
|
||||
t.Error("string should be true")
|
||||
}
|
||||
if valid.Required(0, "zero").Ok {
|
||||
t.Error("Integer should not be equal 0")
|
||||
}
|
||||
if !valid.Required(1, "int").Ok {
|
||||
t.Error("Integer except 0 should be true")
|
||||
}
|
||||
if !valid.Required(time.Now(), "time").Ok {
|
||||
t.Error("time should be true")
|
||||
}
|
||||
if valid.Required([]string{}, "emptySlice").Ok {
|
||||
t.Error("empty slice should be false")
|
||||
}
|
||||
if !valid.Required([]interface{}{"ok"}, "slice").Ok {
|
||||
t.Error("slice should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMin(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Min(-1, 0, "min0").Ok {
|
||||
t.Error("-1 is less than the minimum value of 0 should be false")
|
||||
}
|
||||
if !valid.Min(1, 0, "min0").Ok {
|
||||
t.Error("1 is greater or equal than the minimum value of 0 should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMax(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Max(1, 0, "max0").Ok {
|
||||
t.Error("1 is greater than the minimum value of 0 should be false")
|
||||
}
|
||||
if !valid.Max(-1, 0, "max0").Ok {
|
||||
t.Error("-1 is less or equal than the maximum value of 0 should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRange(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Range(-1, 0, 1, "range0_1").Ok {
|
||||
t.Error("-1 is between 0 and 1 should be false")
|
||||
}
|
||||
if !valid.Range(1, 0, 1, "range0_1").Ok {
|
||||
t.Error("1 is between 0 and 1 should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinSize(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.MinSize("", 1, "minSize1").Ok {
|
||||
t.Error("the length of \"\" is less than the minimum value of 1 should be false")
|
||||
}
|
||||
if !valid.MinSize("ok", 1, "minSize1").Ok {
|
||||
t.Error("the length of \"ok\" is greater or equal than the minimum value of 1 should be true")
|
||||
}
|
||||
if valid.MinSize([]string{}, 1, "minSize1").Ok {
|
||||
t.Error("the length of empty slice is less than the minimum value of 1 should be false")
|
||||
}
|
||||
if !valid.MinSize([]interface{}{"ok"}, 1, "minSize1").Ok {
|
||||
t.Error("the length of [\"ok\"] is greater or equal than the minimum value of 1 should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxSize(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.MaxSize("ok", 1, "maxSize1").Ok {
|
||||
t.Error("the length of \"ok\" is greater than the maximum value of 1 should be false")
|
||||
}
|
||||
if !valid.MaxSize("", 1, "maxSize1").Ok {
|
||||
t.Error("the length of \"\" is less or equal than the maximum value of 1 should be true")
|
||||
}
|
||||
if valid.MaxSize([]interface{}{"ok", false}, 1, "maxSize1").Ok {
|
||||
t.Error("the length of [\"ok\", false] is greater than the maximum value of 1 should be false")
|
||||
}
|
||||
if !valid.MaxSize([]string{}, 1, "maxSize1").Ok {
|
||||
t.Error("the length of empty slice is less or equal than the maximum value of 1 should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLength(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Length("", 1, "length1").Ok {
|
||||
t.Error("the length of \"\" must equal 1 should be false")
|
||||
}
|
||||
if !valid.Length("1", 1, "length1").Ok {
|
||||
t.Error("the length of \"1\" must equal 1 should be true")
|
||||
}
|
||||
if valid.Length([]string{}, 1, "length1").Ok {
|
||||
t.Error("the length of empty slice must equal 1 should be false")
|
||||
}
|
||||
if !valid.Length([]interface{}{"ok"}, 1, "length1").Ok {
|
||||
t.Error("the length of [\"ok\"] must equal 1 should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlpha(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Alpha("a,1-@ $", "alpha").Ok {
|
||||
t.Error("\"a,1-@ $\" are valid alpha characters should be false")
|
||||
}
|
||||
if !valid.Alpha("abCD", "alpha").Ok {
|
||||
t.Error("\"abCD\" are valid alpha characters should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumeric(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Numeric("a,1-@ $", "numeric").Ok {
|
||||
t.Error("\"a,1-@ $\" are valid numeric characters should be false")
|
||||
}
|
||||
if !valid.Numeric("1234", "numeric").Ok {
|
||||
t.Error("\"1234\" are valid numeric characters should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlphaNumeric(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.AlphaNumeric("a,1-@ $", "alphaNumeric").Ok {
|
||||
t.Error("\"a,1-@ $\" are valid alpha or numeric characters should be false")
|
||||
}
|
||||
if !valid.AlphaNumeric("1234aB", "alphaNumeric").Ok {
|
||||
t.Error("\"1234aB\" are valid alpha or numeric characters should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Match("suchuangji@gmail", regexp.MustCompile(`^\w+@\w+\.\w+$`), "match").Ok {
|
||||
t.Error("\"suchuangji@gmail\" match \"^\\w+@\\w+\\.\\w+$\" should be false")
|
||||
}
|
||||
if !valid.Match("suchuangji@gmail.com", regexp.MustCompile(`^\w+@\w+\.\w+$`), "match").Ok {
|
||||
t.Error("\"suchuangji@gmail\" match \"^\\w+@\\w+\\.\\w+$\" should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoMatch(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.NoMatch("123@gmail", regexp.MustCompile(`[^\w\d]`), "nomatch").Ok {
|
||||
t.Error("\"123@gmail\" not match \"[^\\w\\d]\" should be false")
|
||||
}
|
||||
if !valid.NoMatch("123gmail", regexp.MustCompile(`[^\w\d]`), "match").Ok {
|
||||
t.Error("\"123@gmail\" not match \"[^\\w\\d@]\" should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlphaDash(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.AlphaDash("a,1-@ $", "alphaDash").Ok {
|
||||
t.Error("\"a,1-@ $\" are valid alpha or numeric or dash(-_) characters should be false")
|
||||
}
|
||||
if !valid.AlphaDash("1234aB-_", "alphaDash").Ok {
|
||||
t.Error("\"1234aB\" are valid alpha or numeric or dash(-_) characters should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmail(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Email("not@a email", "email").Ok {
|
||||
t.Error("\"not@a email\" is a valid email address should be false")
|
||||
}
|
||||
if !valid.Email("suchuangji@gmail.com", "email").Ok {
|
||||
t.Error("\"suchuangji@gmail.com\" is a valid email address should be true")
|
||||
}
|
||||
if valid.Email("@suchuangji@gmail.com", "email").Ok {
|
||||
t.Error("\"@suchuangji@gmail.com\" is a valid email address should be false")
|
||||
}
|
||||
if valid.Email("suchuangji@gmail.com ok", "email").Ok {
|
||||
t.Error("\"suchuangji@gmail.com ok\" is a valid email address should be false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIP(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.IP("11.255.255.256", "IP").Ok {
|
||||
t.Error("\"11.255.255.256\" is a valid ip address should be false")
|
||||
}
|
||||
if !valid.IP("01.11.11.11", "IP").Ok {
|
||||
t.Error("\"suchuangji@gmail.com\" is a valid ip address should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBase64(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Base64("suchuangji@gmail.com", "base64").Ok {
|
||||
t.Error("\"suchuangji@gmail.com\" are a valid base64 characters should be false")
|
||||
}
|
||||
if !valid.Base64("c3VjaHVhbmdqaUBnbWFpbC5jb20=", "base64").Ok {
|
||||
t.Error("\"c3VjaHVhbmdqaUBnbWFpbC5jb20=\" are a valid base64 characters should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMobile(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
validMobiles := []string{
|
||||
"19800008888",
|
||||
"18800008888",
|
||||
"18000008888",
|
||||
"8618300008888",
|
||||
"+8614700008888",
|
||||
"17300008888",
|
||||
"+8617100008888",
|
||||
"8617500008888",
|
||||
"8617400008888",
|
||||
"16200008888",
|
||||
"16500008888",
|
||||
"16600008888",
|
||||
"16700008888",
|
||||
"13300008888",
|
||||
"14900008888",
|
||||
"15300008888",
|
||||
"17300008888",
|
||||
"17700008888",
|
||||
"18000008888",
|
||||
"18900008888",
|
||||
"19100008888",
|
||||
"19900008888",
|
||||
"19300008888",
|
||||
"13000008888",
|
||||
"13100008888",
|
||||
"13200008888",
|
||||
"14500008888",
|
||||
"15500008888",
|
||||
"15600008888",
|
||||
"16600008888",
|
||||
"17100008888",
|
||||
"17500008888",
|
||||
"17600008888",
|
||||
"18500008888",
|
||||
"18600008888",
|
||||
"13400008888",
|
||||
"13500008888",
|
||||
"13600008888",
|
||||
"13700008888",
|
||||
"13800008888",
|
||||
"13900008888",
|
||||
"14700008888",
|
||||
"15000008888",
|
||||
"15100008888",
|
||||
"15200008888",
|
||||
"15800008888",
|
||||
"15900008888",
|
||||
"17200008888",
|
||||
"17800008888",
|
||||
"18200008888",
|
||||
"18300008888",
|
||||
"18400008888",
|
||||
"18700008888",
|
||||
"18800008888",
|
||||
"19800008888",
|
||||
}
|
||||
|
||||
for _, m := range validMobiles {
|
||||
if !valid.Mobile(m, "mobile").Ok {
|
||||
t.Error(m + " is a valid mobile phone number should be true")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTel(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Tel("222-00008888", "telephone").Ok {
|
||||
t.Error("\"222-00008888\" is a valid telephone number should be false")
|
||||
}
|
||||
if !valid.Tel("022-70008888", "telephone").Ok {
|
||||
t.Error("\"022-70008888\" is a valid telephone number should be true")
|
||||
}
|
||||
if !valid.Tel("02270008888", "telephone").Ok {
|
||||
t.Error("\"02270008888\" is a valid telephone number should be true")
|
||||
}
|
||||
if !valid.Tel("70008888", "telephone").Ok {
|
||||
t.Error("\"70008888\" is a valid telephone number should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPhone(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Phone("222-00008888", "phone").Ok {
|
||||
t.Error("\"222-00008888\" is a valid phone number should be false")
|
||||
}
|
||||
if !valid.Mobile("+8614700008888", "phone").Ok {
|
||||
t.Error("\"+8614700008888\" is a valid phone number should be true")
|
||||
}
|
||||
if !valid.Tel("02270008888", "phone").Ok {
|
||||
t.Error("\"02270008888\" is a valid phone number should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestZipCode(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.ZipCode("", "zipcode").Ok {
|
||||
t.Error("\"00008888\" is a valid zipcode should be false")
|
||||
}
|
||||
if !valid.ZipCode("536000", "zipcode").Ok {
|
||||
t.Error("\"536000\" is a valid zipcode should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValid(t *testing.T) {
|
||||
type user struct {
|
||||
ID int
|
||||
Name string `valid:"Required;Match(/^(test)?\\w*@(/test/);com$/)"`
|
||||
Age int `valid:"Required;Range(1, 140)"`
|
||||
}
|
||||
valid := Validation{}
|
||||
|
||||
u := user{Name: "test@/test/;com", Age: 40}
|
||||
b, err := valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !b {
|
||||
t.Error("validation should be passed")
|
||||
}
|
||||
|
||||
uptr := &user{Name: "test", Age: 40}
|
||||
valid.Clear()
|
||||
b, err = valid.Valid(uptr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Error("validation should not be passed")
|
||||
}
|
||||
if len(valid.Errors) != 1 {
|
||||
t.Fatalf("valid errors len should be 1 but got %d", len(valid.Errors))
|
||||
}
|
||||
if valid.Errors[0].Key != "Name.Match" {
|
||||
t.Errorf("Message key should be `Name.Match` but got %s", valid.Errors[0].Key)
|
||||
}
|
||||
|
||||
u = user{Name: "test@/test/;com", Age: 180}
|
||||
valid.Clear()
|
||||
b, err = valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Error("validation should not be passed")
|
||||
}
|
||||
if len(valid.Errors) != 1 {
|
||||
t.Fatalf("valid errors len should be 1 but got %d", len(valid.Errors))
|
||||
}
|
||||
if valid.Errors[0].Key != "Age.Range." {
|
||||
t.Errorf("Message key should be `Age.Range` but got %s", valid.Errors[0].Key)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecursiveValid(t *testing.T) {
|
||||
type User struct {
|
||||
ID int
|
||||
Name string `valid:"Required;Match(/^(test)?\\w*@(/test/);com$/)"`
|
||||
Age int `valid:"Required;Range(1, 140)"`
|
||||
}
|
||||
|
||||
type AnonymouseUser struct {
|
||||
ID2 int
|
||||
Name2 string `valid:"Required;Match(/^(test)?\\w*@(/test/);com$/)"`
|
||||
Age2 int `valid:"Required;Range(1, 140)"`
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
Password string `valid:"Required"`
|
||||
U User
|
||||
AnonymouseUser
|
||||
}
|
||||
valid := Validation{}
|
||||
|
||||
u := Account{Password: "abc123_", U: User{}}
|
||||
b, err := valid.RecursiveValid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Error("validation should not be passed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSkipValid(t *testing.T) {
|
||||
type User struct {
|
||||
ID int
|
||||
|
||||
Email string `valid:"Email"`
|
||||
ReqEmail string `valid:"Required;Email"`
|
||||
|
||||
IP string `valid:"IP"`
|
||||
ReqIP string `valid:"Required;IP"`
|
||||
|
||||
Mobile string `valid:"Mobile"`
|
||||
ReqMobile string `valid:"Required;Mobile"`
|
||||
|
||||
Tel string `valid:"Tel"`
|
||||
ReqTel string `valid:"Required;Tel"`
|
||||
|
||||
Phone string `valid:"Phone"`
|
||||
ReqPhone string `valid:"Required;Phone"`
|
||||
|
||||
ZipCode string `valid:"ZipCode"`
|
||||
ReqZipCode string `valid:"Required;ZipCode"`
|
||||
}
|
||||
|
||||
u := User{
|
||||
ReqEmail: "a@a.com",
|
||||
ReqIP: "127.0.0.1",
|
||||
ReqMobile: "18888888888",
|
||||
ReqTel: "02088888888",
|
||||
ReqPhone: "02088888888",
|
||||
ReqZipCode: "510000",
|
||||
}
|
||||
|
||||
valid := Validation{}
|
||||
b, err := valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Fatal("validation should not be passed")
|
||||
}
|
||||
|
||||
valid = Validation{RequiredFirst: true}
|
||||
b, err = valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !b {
|
||||
t.Fatal("validation should be passed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPointer(t *testing.T) {
|
||||
type User struct {
|
||||
ID int
|
||||
|
||||
Email *string `valid:"Email"`
|
||||
ReqEmail *string `valid:"Required;Email"`
|
||||
}
|
||||
|
||||
u := User{
|
||||
ReqEmail: nil,
|
||||
Email: nil,
|
||||
}
|
||||
|
||||
valid := Validation{}
|
||||
b, err := valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Fatal("validation should not be passed")
|
||||
}
|
||||
|
||||
validEmail := "a@a.com"
|
||||
u = User{
|
||||
ReqEmail: &validEmail,
|
||||
Email: nil,
|
||||
}
|
||||
|
||||
valid = Validation{RequiredFirst: true}
|
||||
b, err = valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !b {
|
||||
t.Fatal("validation should be passed")
|
||||
}
|
||||
|
||||
u = User{
|
||||
ReqEmail: &validEmail,
|
||||
Email: nil,
|
||||
}
|
||||
|
||||
valid = Validation{}
|
||||
b, err = valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Fatal("validation should not be passed")
|
||||
}
|
||||
|
||||
invalidEmail := "a@a"
|
||||
u = User{
|
||||
ReqEmail: &validEmail,
|
||||
Email: &invalidEmail,
|
||||
}
|
||||
|
||||
valid = Validation{RequiredFirst: true}
|
||||
b, err = valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Fatal("validation should not be passed")
|
||||
}
|
||||
|
||||
u = User{
|
||||
ReqEmail: &validEmail,
|
||||
Email: &invalidEmail,
|
||||
}
|
||||
|
||||
valid = Validation{}
|
||||
b, err = valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Fatal("validation should not be passed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanSkipAlso(t *testing.T) {
|
||||
type User struct {
|
||||
ID int
|
||||
|
||||
Email string `valid:"Email"`
|
||||
ReqEmail string `valid:"Required;Email"`
|
||||
MatchRange int `valid:"Range(10, 20)"`
|
||||
}
|
||||
|
||||
u := User{
|
||||
ReqEmail: "a@a.com",
|
||||
Email: "",
|
||||
MatchRange: 0,
|
||||
}
|
||||
|
||||
valid := Validation{RequiredFirst: true}
|
||||
b, err := valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Fatal("validation should not be passed")
|
||||
}
|
||||
|
||||
valid = Validation{RequiredFirst: true}
|
||||
valid.CanSkipAlso("Range")
|
||||
b, err = valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !b {
|
||||
t.Fatal("validation should be passed")
|
||||
}
|
||||
|
||||
}
|
739
pkg/infrastructure/validation/validators.go
Normal file
739
pkg/infrastructure/validation/validators.go
Normal file
@ -0,0 +1,739 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/astaxie/beego/pkg/infrastructure/logs"
|
||||
)
|
||||
|
||||
// CanSkipFuncs will skip valid if RequiredFirst is true and the struct field's value is empty
|
||||
var CanSkipFuncs = map[string]struct{}{
|
||||
"Email": {},
|
||||
"IP": {},
|
||||
"Mobile": {},
|
||||
"Tel": {},
|
||||
"Phone": {},
|
||||
"ZipCode": {},
|
||||
}
|
||||
|
||||
// MessageTmpls store commond validate template
|
||||
var MessageTmpls = map[string]string{
|
||||
"Required": "Can not be empty",
|
||||
"Min": "Minimum is %d",
|
||||
"Max": "Maximum is %d",
|
||||
"Range": "Range is %d to %d",
|
||||
"MinSize": "Minimum size is %d",
|
||||
"MaxSize": "Maximum size is %d",
|
||||
"Length": "Required length is %d",
|
||||
"Alpha": "Must be valid alpha characters",
|
||||
"Numeric": "Must be valid numeric characters",
|
||||
"AlphaNumeric": "Must be valid alpha or numeric characters",
|
||||
"Match": "Must match %s",
|
||||
"NoMatch": "Must not match %s",
|
||||
"AlphaDash": "Must be valid alpha or numeric or dash(-_) characters",
|
||||
"Email": "Must be a valid email address",
|
||||
"IP": "Must be a valid ip address",
|
||||
"Base64": "Must be valid base64 characters",
|
||||
"Mobile": "Must be valid mobile number",
|
||||
"Tel": "Must be valid telephone number",
|
||||
"Phone": "Must be valid telephone or mobile phone number",
|
||||
"ZipCode": "Must be valid zipcode",
|
||||
}
|
||||
|
||||
var once sync.Once
|
||||
|
||||
// SetDefaultMessage set default messages
|
||||
// if not set, the default messages are
|
||||
// "Required": "Can not be empty",
|
||||
// "Min": "Minimum is %d",
|
||||
// "Max": "Maximum is %d",
|
||||
// "Range": "Range is %d to %d",
|
||||
// "MinSize": "Minimum size is %d",
|
||||
// "MaxSize": "Maximum size is %d",
|
||||
// "Length": "Required length is %d",
|
||||
// "Alpha": "Must be valid alpha characters",
|
||||
// "Numeric": "Must be valid numeric characters",
|
||||
// "AlphaNumeric": "Must be valid alpha or numeric characters",
|
||||
// "Match": "Must match %s",
|
||||
// "NoMatch": "Must not match %s",
|
||||
// "AlphaDash": "Must be valid alpha or numeric or dash(-_) characters",
|
||||
// "Email": "Must be a valid email address",
|
||||
// "IP": "Must be a valid ip address",
|
||||
// "Base64": "Must be valid base64 characters",
|
||||
// "Mobile": "Must be valid mobile number",
|
||||
// "Tel": "Must be valid telephone number",
|
||||
// "Phone": "Must be valid telephone or mobile phone number",
|
||||
// "ZipCode": "Must be valid zipcode",
|
||||
func SetDefaultMessage(msg map[string]string) {
|
||||
if len(msg) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
once.Do(func() {
|
||||
for name := range msg {
|
||||
MessageTmpls[name] = msg[name]
|
||||
}
|
||||
})
|
||||
logs.Warn(`you must SetDefaultMessage at once`)
|
||||
}
|
||||
|
||||
// Validator interface
|
||||
type Validator interface {
|
||||
IsSatisfied(interface{}) bool
|
||||
DefaultMessage() string
|
||||
GetKey() string
|
||||
GetLimitValue() interface{}
|
||||
}
|
||||
|
||||
// Required struct
|
||||
type Required struct {
|
||||
Key string
|
||||
}
|
||||
|
||||
// IsSatisfied judge whether obj has value
|
||||
func (r Required) IsSatisfied(obj interface{}) bool {
|
||||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if str, ok := obj.(string); ok {
|
||||
return len(strings.TrimSpace(str)) > 0
|
||||
}
|
||||
if _, ok := obj.(bool); ok {
|
||||
return true
|
||||
}
|
||||
if i, ok := obj.(int); ok {
|
||||
return i != 0
|
||||
}
|
||||
if i, ok := obj.(uint); ok {
|
||||
return i != 0
|
||||
}
|
||||
if i, ok := obj.(int8); ok {
|
||||
return i != 0
|
||||
}
|
||||
if i, ok := obj.(uint8); ok {
|
||||
return i != 0
|
||||
}
|
||||
if i, ok := obj.(int16); ok {
|
||||
return i != 0
|
||||
}
|
||||
if i, ok := obj.(uint16); ok {
|
||||
return i != 0
|
||||
}
|
||||
if i, ok := obj.(uint32); ok {
|
||||
return i != 0
|
||||
}
|
||||
if i, ok := obj.(int32); ok {
|
||||
return i != 0
|
||||
}
|
||||
if i, ok := obj.(int64); ok {
|
||||
return i != 0
|
||||
}
|
||||
if i, ok := obj.(uint64); ok {
|
||||
return i != 0
|
||||
}
|
||||
if t, ok := obj.(time.Time); ok {
|
||||
return !t.IsZero()
|
||||
}
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() == reflect.Slice {
|
||||
return v.Len() > 0
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// DefaultMessage return the default error message
|
||||
func (r Required) DefaultMessage() string {
|
||||
return MessageTmpls["Required"]
|
||||
}
|
||||
|
||||
// GetKey return the r.Key
|
||||
func (r Required) GetKey() string {
|
||||
return r.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return nil now
|
||||
func (r Required) GetLimitValue() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Min check struct
|
||||
type Min struct {
|
||||
Min int
|
||||
Key string
|
||||
}
|
||||
|
||||
// IsSatisfied judge whether obj is valid
|
||||
// not support int64 on 32-bit platform
|
||||
func (m Min) IsSatisfied(obj interface{}) bool {
|
||||
var v int
|
||||
switch obj.(type) {
|
||||
case int64:
|
||||
if wordsize == 32 {
|
||||
return false
|
||||
}
|
||||
v = int(obj.(int64))
|
||||
case int:
|
||||
v = obj.(int)
|
||||
case int32:
|
||||
v = int(obj.(int32))
|
||||
case int16:
|
||||
v = int(obj.(int16))
|
||||
case int8:
|
||||
v = int(obj.(int8))
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return v >= m.Min
|
||||
}
|
||||
|
||||
// DefaultMessage return the default min error message
|
||||
func (m Min) DefaultMessage() string {
|
||||
return fmt.Sprintf(MessageTmpls["Min"], m.Min)
|
||||
}
|
||||
|
||||
// GetKey return the m.Key
|
||||
func (m Min) GetKey() string {
|
||||
return m.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value, Min
|
||||
func (m Min) GetLimitValue() interface{} {
|
||||
return m.Min
|
||||
}
|
||||
|
||||
// Max validate struct
|
||||
type Max struct {
|
||||
Max int
|
||||
Key string
|
||||
}
|
||||
|
||||
// IsSatisfied judge whether obj is valid
|
||||
// not support int64 on 32-bit platform
|
||||
func (m Max) IsSatisfied(obj interface{}) bool {
|
||||
var v int
|
||||
switch obj.(type) {
|
||||
case int64:
|
||||
if wordsize == 32 {
|
||||
return false
|
||||
}
|
||||
v = int(obj.(int64))
|
||||
case int:
|
||||
v = obj.(int)
|
||||
case int32:
|
||||
v = int(obj.(int32))
|
||||
case int16:
|
||||
v = int(obj.(int16))
|
||||
case int8:
|
||||
v = int(obj.(int8))
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return v <= m.Max
|
||||
}
|
||||
|
||||
// DefaultMessage return the default max error message
|
||||
func (m Max) DefaultMessage() string {
|
||||
return fmt.Sprintf(MessageTmpls["Max"], m.Max)
|
||||
}
|
||||
|
||||
// GetKey return the m.Key
|
||||
func (m Max) GetKey() string {
|
||||
return m.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value, Max
|
||||
func (m Max) GetLimitValue() interface{} {
|
||||
return m.Max
|
||||
}
|
||||
|
||||
// Range Requires an integer to be within Min, Max inclusive.
|
||||
type Range struct {
|
||||
Min
|
||||
Max
|
||||
Key string
|
||||
}
|
||||
|
||||
// IsSatisfied judge whether obj is valid
|
||||
// not support int64 on 32-bit platform
|
||||
func (r Range) IsSatisfied(obj interface{}) bool {
|
||||
return r.Min.IsSatisfied(obj) && r.Max.IsSatisfied(obj)
|
||||
}
|
||||
|
||||
// DefaultMessage return the default Range error message
|
||||
func (r Range) DefaultMessage() string {
|
||||
return fmt.Sprintf(MessageTmpls["Range"], r.Min.Min, r.Max.Max)
|
||||
}
|
||||
|
||||
// GetKey return the m.Key
|
||||
func (r Range) GetKey() string {
|
||||
return r.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value, Max
|
||||
func (r Range) GetLimitValue() interface{} {
|
||||
return []int{r.Min.Min, r.Max.Max}
|
||||
}
|
||||
|
||||
// MinSize Requires an array or string to be at least a given length.
|
||||
type MinSize struct {
|
||||
Min int
|
||||
Key string
|
||||
}
|
||||
|
||||
// IsSatisfied judge whether obj is valid
|
||||
func (m MinSize) IsSatisfied(obj interface{}) bool {
|
||||
if str, ok := obj.(string); ok {
|
||||
return utf8.RuneCountInString(str) >= m.Min
|
||||
}
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() == reflect.Slice {
|
||||
return v.Len() >= m.Min
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DefaultMessage return the default MinSize error message
|
||||
func (m MinSize) DefaultMessage() string {
|
||||
return fmt.Sprintf(MessageTmpls["MinSize"], m.Min)
|
||||
}
|
||||
|
||||
// GetKey return the m.Key
|
||||
func (m MinSize) GetKey() string {
|
||||
return m.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (m MinSize) GetLimitValue() interface{} {
|
||||
return m.Min
|
||||
}
|
||||
|
||||
// MaxSize Requires an array or string to be at most a given length.
|
||||
type MaxSize struct {
|
||||
Max int
|
||||
Key string
|
||||
}
|
||||
|
||||
// IsSatisfied judge whether obj is valid
|
||||
func (m MaxSize) IsSatisfied(obj interface{}) bool {
|
||||
if str, ok := obj.(string); ok {
|
||||
return utf8.RuneCountInString(str) <= m.Max
|
||||
}
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() == reflect.Slice {
|
||||
return v.Len() <= m.Max
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DefaultMessage return the default MaxSize error message
|
||||
func (m MaxSize) DefaultMessage() string {
|
||||
return fmt.Sprintf(MessageTmpls["MaxSize"], m.Max)
|
||||
}
|
||||
|
||||
// GetKey return the m.Key
|
||||
func (m MaxSize) GetKey() string {
|
||||
return m.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (m MaxSize) GetLimitValue() interface{} {
|
||||
return m.Max
|
||||
}
|
||||
|
||||
// Length Requires an array or string to be exactly a given length.
|
||||
type Length struct {
|
||||
N int
|
||||
Key string
|
||||
}
|
||||
|
||||
// IsSatisfied judge whether obj is valid
|
||||
func (l Length) IsSatisfied(obj interface{}) bool {
|
||||
if str, ok := obj.(string); ok {
|
||||
return utf8.RuneCountInString(str) == l.N
|
||||
}
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() == reflect.Slice {
|
||||
return v.Len() == l.N
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DefaultMessage return the default Length error message
|
||||
func (l Length) DefaultMessage() string {
|
||||
return fmt.Sprintf(MessageTmpls["Length"], l.N)
|
||||
}
|
||||
|
||||
// GetKey return the m.Key
|
||||
func (l Length) GetKey() string {
|
||||
return l.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (l Length) GetLimitValue() interface{} {
|
||||
return l.N
|
||||
}
|
||||
|
||||
// Alpha check the alpha
|
||||
type Alpha struct {
|
||||
Key string
|
||||
}
|
||||
|
||||
// IsSatisfied judge whether obj is valid
|
||||
func (a Alpha) IsSatisfied(obj interface{}) bool {
|
||||
if str, ok := obj.(string); ok {
|
||||
for _, v := range str {
|
||||
if ('Z' < v || v < 'A') && ('z' < v || v < 'a') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DefaultMessage return the default Length error message
|
||||
func (a Alpha) DefaultMessage() string {
|
||||
return MessageTmpls["Alpha"]
|
||||
}
|
||||
|
||||
// GetKey return the m.Key
|
||||
func (a Alpha) GetKey() string {
|
||||
return a.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (a Alpha) GetLimitValue() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Numeric check number
|
||||
type Numeric struct {
|
||||
Key string
|
||||
}
|
||||
|
||||
// IsSatisfied judge whether obj is valid
|
||||
func (n Numeric) IsSatisfied(obj interface{}) bool {
|
||||
if str, ok := obj.(string); ok {
|
||||
for _, v := range str {
|
||||
if '9' < v || v < '0' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DefaultMessage return the default Length error message
|
||||
func (n Numeric) DefaultMessage() string {
|
||||
return MessageTmpls["Numeric"]
|
||||
}
|
||||
|
||||
// GetKey return the n.Key
|
||||
func (n Numeric) GetKey() string {
|
||||
return n.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (n Numeric) GetLimitValue() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AlphaNumeric check alpha and number
|
||||
type AlphaNumeric struct {
|
||||
Key string
|
||||
}
|
||||
|
||||
// IsSatisfied judge whether obj is valid
|
||||
func (a AlphaNumeric) IsSatisfied(obj interface{}) bool {
|
||||
if str, ok := obj.(string); ok {
|
||||
for _, v := range str {
|
||||
if ('Z' < v || v < 'A') && ('z' < v || v < 'a') && ('9' < v || v < '0') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DefaultMessage return the default Length error message
|
||||
func (a AlphaNumeric) DefaultMessage() string {
|
||||
return MessageTmpls["AlphaNumeric"]
|
||||
}
|
||||
|
||||
// GetKey return the a.Key
|
||||
func (a AlphaNumeric) GetKey() string {
|
||||
return a.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (a AlphaNumeric) GetLimitValue() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Match Requires a string to match a given regex.
|
||||
type Match struct {
|
||||
Regexp *regexp.Regexp
|
||||
Key string
|
||||
}
|
||||
|
||||
// IsSatisfied judge whether obj is valid
|
||||
func (m Match) IsSatisfied(obj interface{}) bool {
|
||||
return m.Regexp.MatchString(fmt.Sprintf("%v", obj))
|
||||
}
|
||||
|
||||
// DefaultMessage return the default Match error message
|
||||
func (m Match) DefaultMessage() string {
|
||||
return fmt.Sprintf(MessageTmpls["Match"], m.Regexp.String())
|
||||
}
|
||||
|
||||
// GetKey return the m.Key
|
||||
func (m Match) GetKey() string {
|
||||
return m.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (m Match) GetLimitValue() interface{} {
|
||||
return m.Regexp.String()
|
||||
}
|
||||
|
||||
// NoMatch Requires a string to not match a given regex.
|
||||
type NoMatch struct {
|
||||
Match
|
||||
Key string
|
||||
}
|
||||
|
||||
// IsSatisfied judge whether obj is valid
|
||||
func (n NoMatch) IsSatisfied(obj interface{}) bool {
|
||||
return !n.Match.IsSatisfied(obj)
|
||||
}
|
||||
|
||||
// DefaultMessage return the default NoMatch error message
|
||||
func (n NoMatch) DefaultMessage() string {
|
||||
return fmt.Sprintf(MessageTmpls["NoMatch"], n.Regexp.String())
|
||||
}
|
||||
|
||||
// GetKey return the n.Key
|
||||
func (n NoMatch) GetKey() string {
|
||||
return n.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (n NoMatch) GetLimitValue() interface{} {
|
||||
return n.Regexp.String()
|
||||
}
|
||||
|
||||
var alphaDashPattern = regexp.MustCompile(`[^\d\w-_]`)
|
||||
|
||||
// AlphaDash check not Alpha
|
||||
type AlphaDash struct {
|
||||
NoMatch
|
||||
Key string
|
||||
}
|
||||
|
||||
// DefaultMessage return the default AlphaDash error message
|
||||
func (a AlphaDash) DefaultMessage() string {
|
||||
return MessageTmpls["AlphaDash"]
|
||||
}
|
||||
|
||||
// GetKey return the n.Key
|
||||
func (a AlphaDash) GetKey() string {
|
||||
return a.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (a AlphaDash) GetLimitValue() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var emailPattern = regexp.MustCompile(`^[\w!#$%&'*+/=?^_` + "`" + `{|}~-]+(?:\.[\w!#$%&'*+/=?^_` + "`" + `{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[a-zA-Z0-9](?:[\w-]*[\w])?$`)
|
||||
|
||||
// Email check struct
|
||||
type Email struct {
|
||||
Match
|
||||
Key string
|
||||
}
|
||||
|
||||
// DefaultMessage return the default Email error message
|
||||
func (e Email) DefaultMessage() string {
|
||||
return MessageTmpls["Email"]
|
||||
}
|
||||
|
||||
// GetKey return the n.Key
|
||||
func (e Email) GetKey() string {
|
||||
return e.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (e Email) GetLimitValue() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var ipPattern = regexp.MustCompile(`^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$`)
|
||||
|
||||
// IP check struct
|
||||
type IP struct {
|
||||
Match
|
||||
Key string
|
||||
}
|
||||
|
||||
// DefaultMessage return the default IP error message
|
||||
func (i IP) DefaultMessage() string {
|
||||
return MessageTmpls["IP"]
|
||||
}
|
||||
|
||||
// GetKey return the i.Key
|
||||
func (i IP) GetKey() string {
|
||||
return i.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (i IP) GetLimitValue() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var base64Pattern = regexp.MustCompile(`^(?:[A-Za-z0-99+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$`)
|
||||
|
||||
// Base64 check struct
|
||||
type Base64 struct {
|
||||
Match
|
||||
Key string
|
||||
}
|
||||
|
||||
// DefaultMessage return the default Base64 error message
|
||||
func (b Base64) DefaultMessage() string {
|
||||
return MessageTmpls["Base64"]
|
||||
}
|
||||
|
||||
// GetKey return the b.Key
|
||||
func (b Base64) GetKey() string {
|
||||
return b.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (b Base64) GetLimitValue() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// just for chinese mobile phone number
|
||||
var mobilePattern = regexp.MustCompile(`^((\+86)|(86))?1([356789][0-9]|4[579]|6[67]|7[0135678]|9[189])[0-9]{8}$`)
|
||||
|
||||
// Mobile check struct
|
||||
type Mobile struct {
|
||||
Match
|
||||
Key string
|
||||
}
|
||||
|
||||
// DefaultMessage return the default Mobile error message
|
||||
func (m Mobile) DefaultMessage() string {
|
||||
return MessageTmpls["Mobile"]
|
||||
}
|
||||
|
||||
// GetKey return the m.Key
|
||||
func (m Mobile) GetKey() string {
|
||||
return m.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (m Mobile) GetLimitValue() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// just for chinese telephone number
|
||||
var telPattern = regexp.MustCompile(`^(0\d{2,3}(\-)?)?\d{7,8}$`)
|
||||
|
||||
// Tel check telephone struct
|
||||
type Tel struct {
|
||||
Match
|
||||
Key string
|
||||
}
|
||||
|
||||
// DefaultMessage return the default Tel error message
|
||||
func (t Tel) DefaultMessage() string {
|
||||
return MessageTmpls["Tel"]
|
||||
}
|
||||
|
||||
// GetKey return the t.Key
|
||||
func (t Tel) GetKey() string {
|
||||
return t.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (t Tel) GetLimitValue() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Phone just for chinese telephone or mobile phone number
|
||||
type Phone struct {
|
||||
Mobile
|
||||
Tel
|
||||
Key string
|
||||
}
|
||||
|
||||
// IsSatisfied judge whether obj is valid
|
||||
func (p Phone) IsSatisfied(obj interface{}) bool {
|
||||
return p.Mobile.IsSatisfied(obj) || p.Tel.IsSatisfied(obj)
|
||||
}
|
||||
|
||||
// DefaultMessage return the default Phone error message
|
||||
func (p Phone) DefaultMessage() string {
|
||||
return MessageTmpls["Phone"]
|
||||
}
|
||||
|
||||
// GetKey return the p.Key
|
||||
func (p Phone) GetKey() string {
|
||||
return p.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (p Phone) GetLimitValue() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// just for chinese zipcode
|
||||
var zipCodePattern = regexp.MustCompile(`^[1-9]\d{5}$`)
|
||||
|
||||
// ZipCode check the zip struct
|
||||
type ZipCode struct {
|
||||
Match
|
||||
Key string
|
||||
}
|
||||
|
||||
// DefaultMessage return the default Zip error message
|
||||
func (z ZipCode) DefaultMessage() string {
|
||||
return MessageTmpls["ZipCode"]
|
||||
}
|
||||
|
||||
// GetKey return the z.Key
|
||||
func (z ZipCode) GetKey() string {
|
||||
return z.Key
|
||||
}
|
||||
|
||||
// GetLimitValue return the limit value
|
||||
func (z ZipCode) GetLimitValue() interface{} {
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user