介绍一个golang struct 校验工具【qvalid】

这个库是去年写的,主要用于结构体的校验。
比如字符串长度、数值大小、oneof,以及字符串的一些常见属性(email/url/ip)等。
项目的地址:https://github.com/miaomiao3/qvalid
因为不想用一些现成但很臃肿的库如govalidator,就自己写了一个,写的时候希望语法尽可能精简,借用了mongodb的一些约束,比如lt/lte gt/gte概念,来约束字符串的长度或者数值的值大小。由于开发的时候是针对某些特定场合用的,所以完备性是有待提高的(主要的代码大概花了半天那样子,就是说可能有bug哈,如果有兴趣可以提issue和pr)。但是基本功能是没问题的。
下面内容摘自repo的readme,懒得翻成中文了,将就看一下吧。

qvalid

powerful tool to validate struct’s exported fields

Features

  • validate field value of numbers(int/uint/float…)
  • validate field length of string/array/slice/map
  • support in check
  • when a field is slice and its element is struct/struct_pointer, qvalid auto validate this struct related element
  • when a field is string, support attribute check. e.g. email/ip/email…
  • pretty field output msg, use json tag first as field name

Install

go get -u github.com/miaomiao3/qvalid

Syntax

  • As for bound limit, it means length of string/array/slice/map, and value of numbers(int/uint/float…)
  • If ‘in’ was set, do not set bound limit
  • , and = reserved
  • [ and ] reserved except of in constraint

constraint description

constraintdescriptioncomment
ltlittle than, upper bound limitu can set lt or lte!
ltelittle than or equal, upper bound limitu can set lt or lte!
gtgreater than, lower bound limitu can set gt or gte!
gtegreater than or equal, lower bound limitu can set gt or gte!
inmust in one of the list item. item character must be numeric or alphaIf ‘in’ was set, do not set bound limit
attrwhen the field is string, it works to some known attribute like email, ip .etcattr desc

attr is available as below

const (
	StringTypeEmail        = "email"
	StringTypeAlpha        = "alpha"
	StringTypeUpperAlpha   = "upper_alpha"
	StringTypeLowerAlpha   = "lower_alpha"
	StringTypeAlphaNumeric = "alpha_numeric"
	StringTypeNumeric      = "numeric"
	StringTypeInt          = "int"
	StringTypeFloat        = "float"
	StringTypeHex          = "hex"
	StringTypeAscii        = "ascii"
	StringTypeVisibleAscii = "visible_ascii"
	StringTypeBytes        = "bytes"
	StringTypeBase64       = "base64"
	StringTypeDNS          = "dns"
	StringTypeVersion      = "version"
	StringTypeIp           = "ip"
	StringTypePort         = "port"
	StringTypeURL          = "url"
)

Examples

First, define some struct:


type Dog struct {
	Name      string            `valid:"in=[rose,tulip]" json:"name"`
	Color     string            `valid:"lt=5, gte=3" json:"color"`
	Weight    float64           `valid:"lt=100, gte=10" json:"weight"`
	Clothes   int               `valid:"in=[1,3,5]" json:"clothes"`
	NickNames []string          `valid:"lt=5, gt=1"`
	Relations map[string]string `valid:"lt=5, gt=1"`
	Email     string            `valid:"attr=email"`
	from      string            `json:"from" valid:"lt=10, gt=1"` // unexported, will be ignored by qvalid
}

type BadTag struct {
	Err1 string `valid:"lt=10, lte=1"`            // this will cause [qvalid] error msg
	Err2 string `valid:"gt=10, gte=1"`            // this will cause [qvalid] error msg
	Err3 string `valid:"lt=10, gt=1, in=[aa,bb]"` // this will cause [qvalid] error msg
	Err4 string `valid:"lt=1, gte=1"`             // this will cause [qvalid] error msg
}

type FakeFood struct {
	Leaf     Leaf
	MainLeaf *Leaf
}

type Food struct {
	Leafs []Leaf `valid:"gte=1"`
}

type Leaf struct {
	Name string `valid:"in=[rose,tulip]" json:"name"`
}


validate struct with simple field

like Dog


func validateSimpleField() {
	dog := &Dog{}
	isPass, validErrors := qvalid.ValidateStruct(dog)
	fmt.Println("validateSimpleField")
	checkAndDumpValidErrors(isPass, validErrors)

	newFlower := &Dog{
		Name:      "rose",
		Color:     "gray",
		Weight:    30.0,
		Clothes:   3,
		NickNames: []string{"wangcai", "dawang"},
		Relations: map[string]string{
			"owner": "cy",
			"birth": "2018",
		},
		Email: "google@gmail.com",
	}
	isPass, validErrors = qvalid.ValidateStruct(newFlower)
	checkAndDumpValidErrors(isPass, validErrors)
}

output:

validateSimpleField
    illegal input and result:
        isPass:false
        validErrors:
            err:0 --> &{Field:.name Msg:value: not in:[rose tulip]}
            err:1 --> &{Field:.color Msg:expect length >= 3 but get length: 0}
            err:2 --> &{Field:.weight Msg:expect value >= 10 but get value:0}
            err:3 --> &{Field:.clothes Msg:value:0 not in:[1 3 5]}
            err:4 --> &{Field:.NickNames Msg:expect length > 1 but get length: 0}
            err:5 --> &{Field:.Relations Msg:expect length > 1 but get length: 0}
            err:6 --> &{Field:.Email Msg:value: not match attribute:email}

    legal input and result:
        isPass:true

validate embedded struct

like FakeFood


func validateEmbedStruct() {
	food := &FakeFood{
		MainLeaf: &Leaf{},
	}
	isPass, validErrors := qvalid.ValidateStruct(food)
	fmt.Println("validateEmbedStruct")
	checkAndDumpValidErrors(isPass, validErrors)

	newFakeFood := FakeFood{
		Leaf: Leaf{
			Name: "rose",
		},
		MainLeaf: &Leaf{
			Name: "rose",
		},
	}
	isPass, validErrors = qvalid.ValidateStruct(newFakeFood)
	checkAndDumpValidErrors(isPass, validErrors)
}

output


validateEmbedStruct
    illegal input and result:
        isPass:false
        validErrors:
            err:0 --> &{Field:.Leaf.name Msg:value: not in:[rose tulip]}
            err:1 --> &{Field:.MainLeaf.name Msg:value: not in:[rose tulip]}

    legal input and result:
        isPass:true
        

validate slice embedded struct

like Food


func validateSliceEmbedStruct() {
	food := &Food{
		Leafs: []Leaf{ // if Leafs is empty, qvalid do not check empty slice field, so set 1 element to test
			Leaf{},
		},
	}
	isPass, validErrors := qvalid.ValidateStruct(food)
	fmt.Println("validateSliceEmbedStruct")
	checkAndDumpValidErrors(isPass, validErrors)

	newFood := Food{
		Leafs: []Leaf{
			Leaf{
				Name: "rose",
			},
		},
	}
	isPass, validErrors = qvalid.ValidateStruct(newFood)
	checkAndDumpValidErrors(isPass, validErrors)
}

output


validateSliceEmbedStruct
    illegal input and result:
        isPass:false
        validErrors:
            err:0 --> &{Field:.Leafs[0].name Msg:value: not in:[rose tulip]}

    legal input and result:
        isPass:true
        

sample of bad tag

like Person


func badTag() {
	bad := &BadTag{}

	isPass, validErrors := qvalid.ValidateStruct(bad)
	fmt.Println("badTag")
	checkAndDumpValidErrors(isPass, validErrors)
}

output


badTag
    illegal input and result:
        isPass:false
        validErrors:
            err:0 --> &{Field:[qvalid] GetConstraintFromTag Msg:lt and lte can't both set}
            err:1 --> &{Field:[qvalid] GetConstraintFromTag Msg:gt and gt can't both set}
            err:2 --> &{Field:[qvalid] GetConstraintFromTag Msg:bound limit and 'in' can't both set}
            err:3 --> &{Field:[qvalid] GetConstraintFromTag Msg:upper and lower bound limit illegal}
            

for more details, see example dir.

TODO:

  1. check pointer loop
  2. customized field validator
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值