英文:
Golang validator multifield dependency
问题
我想验证以下结构:
type CarModel struct {
gorm.Model
OwnerID int `json:"ownerid" validate:"nonzero"`
Type string `json:"type" validate:"regexp=(?)(A|B)"`
A string `json:"url" validate:"isurl"`
B string `json:"ip" validate:"isip"`
}
我想根据Type验证A和B:
- 如果Type为A,则A必须存在且必须是一个URL,但B不能存在。
- 如果Type为B,则A不能存在且B必须是一个IP。
使用验证器(validator)可以实现这个吗?
我尝试了自定义验证,但无法找到一种方法来获取Type的值:
func checkB(v interface{}, param string) error {
theB := reflect.ValueOf(v)
if theB.Kind() != reflect.String {
return validator.ErrUnsupported
}
// 检查B是否为IP
ipcool := net.ParseIP(theB.String())
if ipcool == nil {
return errors.New("B : ip incorrecte " + theB.String())
}
return nil
}
根据Alex Nichol的回答,首先感谢您的帮助。
如果我理解正确,我需要遍历所有的"validate"字段,以保留TYPE、A和B的值,并根据TYPE进行检查...
我做了这个:
func checkMonitor(v interface{}) error {
var mytype string
var myA string
var myB string
val := reflect.ValueOf(v)
// 遍历字段
for i := 0; i < val.NumField(); i++ {
// 查找validate标签
field := val.Type().Field(i)
tags := field.Tag
_, ok := tags.Lookup("validate")
if !ok {
// 没有validate标签。
continue
}
// 获取字段的值。
fieldValue := val.Field(i)
switch field.Name {
case "Type":
mytype = fieldValue.String()
case "A":
myA = fieldValue.String()
case "B":
myB = fieldValue.String()
}
// 在这里进行验证逻辑。
//fmt.Println("field", field.Name, "has validate tag", validate, "and value", fieldValue.Interface())
}
if mytype == "A" {
if myA == "" {
return errors.New("A vide et type A")
}
ipcool := net.ParseIP(myA)
if ipcool == nil {
return errors.New("A incorrecte " + myA)
}
} else if mytype == "HTML" {
if myB == "" {
return errors.New("B vide et type B")
}
_, urlpascool := url.ParseRequestURI(myB)
if urlpascool != nil {
return errors.New("B incorrecte " + myB)
}
}
return nil
}
但在switch case中的mytype、myA和myB处出现错误:
cannot use fieldValue.Interface() (type interface {}) as type string in assignment: need type assertion
编辑:
只需要动动脑筋:
switch field.Name {
case "Type":
mytype = fieldValue.String()
case "A":
myA = fieldValue.String()
case "B":
myB = fieldValue.String()
}
英文:
I'd like to validate the following structure :
> type CarModel struct {
> gorm.Model
> OwnerID int json:"ownerid" validate:"nonzero"
> Type string json:"type" validate:"regexp=(?)(A|B)"
> A string json:"url" validate:"isurl"
> B string json:"ip" validate:"isip"
> }
I would like to validate A and B depending on Type,
if type = A then A must exist and must be a URL BUT B must not exist
if type = B then A must not exist and B must be an IP
is this possible with validator ?
I did try custom validation but I cannot find a way to see type value :
func checkB(v interface{}, param string) error {
theB := reflect.ValueOf(v)
if theB.Kind() != reflect.String {
return validator.ErrUnsupported
}
//check if B is an IP
ipcool := net.ParseIP(theB.String())
if ipcool == nil {
return errors.New("B : ip incorrecte " + theB.String())
}
return nil
}
Upon the answer of Alex Nichol, I would like first to thank you for your help.
If I understood correctly, I would have to iterate through all the "validate" fields, to keep a trace of the value of TYPE, A and B and then to check them depending on TYPE ...
I did this :
func checkMonitor(v interface{}) error {
var mytype string
var myA string
var myB string
val := reflect.ValueOf(v)
// Iterate through fields
for i := 0; i < val.NumField(); i++ {
// Lookup the validate tag
field := val.Type().Field(i)
tags := field.Tag
_, ok := tags.Lookup("validate")
if !ok {
// No validate tag.
continue
}
// Get the value of the field.
fieldValue := val.Field(i)
switch field.Name {
case "Type":
mytype = fieldValue.Interface()
case "A":
myA = fieldValue.Interface()
case "B":
myB = fieldValue.Interface()
}
// Validation logic here.
//fmt.Println("field", field.Name, "has validate tag", validate, "and value", fieldValue.Interface())
}
if mytype == "A" {
if myA == "" {
return errors.New("A vide et type A")
}
ipcool := net.ParseIP(myA)
if ipcool == nil {
return errors.New("A incorrecte " + myA)
}
} else if mytype == "HTML" {
if myB == "" {
return errors.New("B vide et type B")
}
_, urlpascool := url.ParseRequestURI(myB)
if urlpascool != nil {
return errors.New("B incorrecte " + myB)
}
}
return nil
}
but got an error on the mytype, myA and myB in the switch case :
cannot use fieldValue.Interface() (type interface {}) as type string in assignment: need type assertion
EDIT :
just needed to use my brain :
switch field.Name {
case "Type":
mytype = fieldValue.String()
case "A":
myA = fieldValue.String()
case "B":
myB = fieldValue.Interface()
}
答案1
得分: 1
你可能想要使用反射来迭代结构体的字段,获取每个字段的validate
标签,并对字段进行检查。这意味着你需要在结构体级别上进行验证。否则,如果你将类似myInstance.OwnerID
的内容传递给函数,你将丢失与之关联的标签。
以下代码循环遍历结构体的字段,并获取每个字段的validate
标签:
func checkStruct(v interface{}) error {
val := reflect.ValueOf(v)
// 遍历字段
for i := 0; i < val.NumField(); i++ {
// 查找 validate 标签
field := val.Type().Field(i)
tags := field.Tag
validate, ok := tags.Lookup("validate")
if !ok {
// 没有 validate 标签
continue
}
// 获取字段的值
fieldValue := val.Field(i)
// 在这里进行验证逻辑
fmt.Println("字段", field.Name, "具有 validate 标签", validate, "和值",
fieldValue.Interface())
}
return nil
}
例如,我们可以将以下CarModel
传递给它:
checkStruct(CarModel{
OwnerID: 2,
Type: "B",
A: "http://google.com",
B: "192.168.1.1",
})
它将打印出以下内容:
字段 OwnerID 具有 validate 标签 nonzero 和值 2
字段 Type 具有 validate 标签 regexp=(?)(A|B) 和值 B
字段 A 具有 validate 标签 isurl 和值 http://google.com
字段 B 具有 validate 标签 isip 和值 192.168.1.1
英文:
You probably want to use reflection to iterate over the fields of the struct, get the validate
tag for each field, and check the field. This means you'll have to do validation on a struct level. Otherwise, if you pass something like myInstance.OwnerID
to a function, you'll lose the tag associated with it.
This code loops through the fields of a struct and gets the validate
tag for each:
func checkStruct(v interface{}) error {
val := reflect.ValueOf(v)
// Iterate through fields
for i := 0; i < val.NumField(); i++ {
// Lookup the validate tag
field := val.Type().Field(i)
tags := field.Tag
validate, ok := tags.Lookup("validate")
if !ok {
// No validate tag.
continue
}
// Get the value of the field.
fieldValue := val.Field(i)
// Validation logic here.
fmt.Println("field", field.Name, "has validate tag", validate, "and value",
fieldValue.Interface())
}
return nil
}
For example, we could pass it the following CarModel
:
checkStruct(CarModel{
OwnerID: 2,
Type: "B",
A: "http://google.com",
B: "192.168.1.1",
})
and it would print out the following:
field OwnerID has validate tag nonzero and value 2
field Type has validate tag regexp=(?)(A|B) and value B
field A has validate tag isurl and value http://google.com
field B has validate tag isip and value 192.168.1.1
答案2
得分: 0
似乎你的验证规则非常复杂,但你可以尝试使用validating。
假设我们已经有了两个自定义的验证器IsURL
和IsIP
,那么你的验证规则可以按照以下方式实现:
import (
"regexp"
v "github.com/RussellLuo/validating"
)
// 自定义验证器
var (
MatchRegexp = func(pattern string) v.Validator {
return v.FromFunc(func(field v.Field) v.Errors {
switch t := field.ValuePtr.(type) {
case *string:
if matched, _ := regexp.MatchString(pattern, *t); !matched {
return v.NewErrors(field.Name, v.ErrInvalid, "不匹配")
}
return nil
default:
return v.NewErrors(field.Name, v.ErrUnsupported, "不支持")
}
})
}
// IsURL 和 IsIP 省略
)
type CarModel struct {
gorm.Model
OwnerID int `json:"ownerid"`
Type string `json:"type"`
A string `json:"url"`
B string `json:"ip"`
}
func main() {
car := CarModel{}
errs := v.Validate(v.Schema{
v.F("ownerid", &car.OwnerID): v.Nonzero(),
v.F("type", &car.Type): MatchRegexp("(A|B)"),
v.F("a", &car.A): v.Lazy(func() v.Validator {
if car.Type == "A" {
return v.All(v.Nonzero(), IsURL())
}
return v.Not(v.Nonzero())
}),
v.F("b", &car.B): v.Lazy(func() v.Validator {
if car.Type == "B" {
return v.All(v.Nonzero(), IsIP())
}
return v.Not(v.Nonzero())
}),
})
}
英文:
Seems like your validation rules are very complicated, but you can give validating a try.
Suppose we already have two customized validators IsURL
and IsIP
, then your validation rules can be implemented as follows:
<!-- language: go -->
import (
"regexp"
v "github.com/RussellLuo/validating"
)
// Customized validators
var (
MatchRegexp = func(pattern string) v.Validator {
return v.FromFunc(func(field v.Field) v.Errors {
switch t := field.ValuePtr.(type) {
case *string:
if matched, _ := regexp.MatchString(pattern, *t); !matched {
return v.NewErrors(field.Name, v.ErrInvalid, "does not match")
}
return nil
default:
return v.NewErrors(field.Name, v.ErrUnsupported, "is unsupported")
}
})
}
// IsURL and IsIP are omitted
)
type CarModel struct {
gorm.Model
OwnerID int `json:"ownerid"`
Type string `json:"type"`
A string `json:"url"`
B string `json:"ip"`
}
func main() {
car := CarModel{}
errs := v.Validate(v.Schema{
v.F("ownerid", &car.OwnerID): v.Nonzero(),
v.F("type", &car.Type): MatchRegexp("(A|B)"),
v.F("a", &car.A): v.Lazy(func() v.Validator {
if car.Type == "A" {
return v.All(v.Nonzero(), IsURL())
}
return v.Not(v.Nonzero())
}),
v.F("b", &car.B): v.Lazy(func() v.Validator {
if car.Type == "B" {
return v.All(v.Nonzero(), IsIP())
}
return v.Not(v.Nonzero())
}),
})
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论