为了避免结构体默认值带来的困惑,从goframe v2.0版本开始,我们增加了一个Assoc方法,用于结构体校验时严格按照给定的参数而不是按照结构体的属性值(避免结构体属性默认值的影响),而校验规则同样会自动读取结构体中的gvalid tag

该特定对接收客户端请求参数校验的场景特别有用。

使用示例

package main

import (
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gctx"
)

func main() {
	type User struct {
		Name string `v:"required#请输入用户姓名"`
		Type int    `v:"required#请选择用户类型"`
	}
	var (
		ctx  = gctx.New()
		user = User{}
		data = g.Map{
			"name": "john",
		}
	)  
	err := g.Validator().Assoc(data).Data(user).Run(ctx)  
	if err != nil {
		g.Dump(err.Items())
	}
}

执行后,终端输出:

[
    {
        "Type": {
            "required": "请选择用户类型"
        }
    }
]

可以看到,结构体中的属性Type校验规则required并没有受到默认值的影响,仍旧被执行了预期的校验检查。






Content Menu

  • No labels

6 Comments

  1. v2.0???发布了吗?

  2. 结构体数组里面的 tag 无法校验,下面代码校验完 err 为 nil

    import (
    	"github.com/gogf/gf/v2/frame/g"
    	"github.com/gogf/gf/v2/os/gctx"
    )
    
    func main() {
    	type Student struct {
    		Name string `json:"name" v:"required"`
    		Age  int    `json:"age" v:"required"`
    	}
    
    	type Teacher struct {
    		Name     string    `json:"name" v:"required"`
    		Students []Student `json:"students" v:"required"`
    	}
    
    	var (
    		ctx     = gctx.New()
    		teacher = Teacher{}
    		data    = g.Map{
    			"name":     "john",
    			"students": `[{"age":2},{"name":"jack", age:4}]`,
    		}
    	)
    
    	err := g.Validator().Assoc(data).Data(teacher).Run(ctx)
    	if err != nil {
    		g.Dump(err.Items())
    	}
    }
    
    
    1. 已修复,下个版本发布。

  3. tag提供校验规则的情况下无法动态设置rule的参数值。比如我需要在校验规则中传入数据库schema参数,以此达到在查数据库校验时能够动态选择数据库,通过rules注册可以实现这个规则,但那只适用于对某个字段的校验,像这种struct整体的校验怎么实现呢?


    var server entity.Server
    //...省略赋值部分
    
    rules := map[string]string{}
    messages := map[string]interface{}{}
    type sServer struct {
    	g.Meta `v:"has-rack-position:%s#在Rack表中不存在Rack='{rack}'且Position='{Postion}的记录'"`
    	entity.Server
    }
    data := &sServer{}
    validator := g.Validator().Rules(rules).Messages(messages).
    	RuleFunc("has-rack-position", assoc.HasRackPositionRule).
    	Data(data)
    validErr := validator.Assoc(server).Run(ctx)
    
    

    由于需要对entity进行校验,按照框架规范,entity是不许修改的。因此我这里使用了组合方式定义了一个新的struct,通过插入g.Meta来定义校验的tag:

    g.Meta `v:"has-rack-position:%s#在Rack表中不存在Rack='{rack}'且Position='{Postion}的记录'"`

    其中有个%s是想通过变量方式放入数据库的schema,然后在assoc.HasRackPositionRule校验规则函数中动态选择数据库。

    比如:g.Meta `v:"has-rack-position:test1#在Rack表中不存在Rack='{rack}'且Position='{Postion}的记录'"`

    这样我就可以在校验规则RuleFunc中 读取 in.Rule, 通过split来获得schema, 然后通过g.DB().Ctx(ctx).Schema(schema)这种方式来切换数据库了。

    但是对于struct整体校验这种情况下,tag无法动态传入某种参数

    1. 我尝试通过使用反射动态创建struct方式,来达到动态设置tag的目的,以下代码测试通过:

      var server entity.Server
      //...省略赋值部分
      
      rules := map[string]string{}
      messages := map[string]interface{}{}
      metaTag := fmt.Sprintf("v:\"has-rack-position:%s#在Rack表中不存在Rack='{rack}'且Position='{Postion}的记录'\"", schema)
      dataStruct := reflect.StructOf([]reflect.StructField{
          {
              Name: "Meta",
              Type: reflect.TypeOf(g.Meta{}),
              Tag:  reflect.StructTag(metaTag),
              Anonymous: true,
          },
          {
              Name: "Server",
              Type: reflect.TypeOf(entity.Server{}),
              Anonymous: true,
          },
      })
      data := reflect.New(dataStruct).Elem().Interface()
      //fmt.Printf("%#v\n", data)
      validator := g.Validator().Rules(rules).Messages(messages).
          RuleFunc("has-rack-position", assoc.HasRackPositionRule).
          Data(data)
      
      validErr := validator.Assoc(server).Run(ctx)

      看起来暂时只能使用这种方式来实现动态传入tag规则执行校验了。

      1. 直接将关系与校验规则存入到map中取不更快捷吗?g.Map{"schema1":"rule1","schema2":assoc.HasRackPositionRule}