2017-02-22 146 views
0

我想验证以下结构:Golang验证多字段依赖性

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"` 
} 

我想验证A和B取决于类型, 如果type = A则A必须存在,并且必须是URL但B必须不存在 如果type = B那么A必须不存在且B必须是IP

这是否可能带有验证器?

我也尝试自定义的验证,但我无法找到一个方法来看看类型值:

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 
} 

在亚历克斯·尼科尔的答案,我想首先感谢你的帮助。

如果我理解正确的话,我会通过所有的“验证”字段进行迭代,以保持TYPE A和B的值的跟踪,然后检查他们根据类型...

我这样做:

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 
} 

,但上了MYTYPE错误,妙和MYB开关壳体:

不能使用fieldValue.Interface()(接口类型{})在指定字符串类型:需要断言

编辑: 只是需要用我的大脑:

switch field.Name { 
case "Type": 
    mytype = fieldValue.String() 
case "A": 
    myA = fieldValue.String() 
case "B": 
    myB = fieldValue.Interface() 
} 

回答

1

你可能想使用反射来遍历结构领域,获取每个字段的validate标记,并检查该字段。这意味着您必须在结构级别进行验证。否则,如果您将诸如myInstance.OwnerID之类的内容传递给函数,则会丢失与其关联的标记。

代码遍历一个struct的领域,并得到每个validate标签:

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 
} 

例如,我们可以通过它下面CarModel

checkStruct(CarModel{ 
    OwnerID: 2, 
    Type: "B", 
    A:  "http://google.com", 
    B:  "192.168.1.1", 
}) 

,它会打印出如下:

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 
0

看起来像你的验证规则非常复杂,但您可以试一试validating

假设我们已经有两个自定义的验证IsURLIsIP,那么你的验证规则可以如下实施:

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()) 
     }), 
    }) 
}