Golang的参数校验,大多数使用的是validator(gin框架使用的是validator v8/v9)。

validator
checkervalidator

使用例子: tag 与 Rule的比较

validator使用的tag,与checker的Rule的对应关系可以参考README文档。

checkerSliceArrayMap

自定义校验规则

使用validator

validatoris-awesome
package main

import (
    "fmt"

    "github.com/go-playground/validator/v10"
)

// MyStruct ..
type MyStruct struct {
    String string `validate:"is-awesome"`
}

// use a single instance of Validate, it caches struct info
var validate *validator.Validate

func main() {
    validate = validator.New()
    validate.RegisterValidation("is-awesome", ValidateMyVal)

    s := MyStruct{String: "not awesome"}
    err := validate.Struct(s)
    if err != nil {
        fmt.Printf("%v", err)
    }
}

// ValidateMyVal implements validator.Func
func ValidateMyVal(fl validator.FieldLevel) bool {
    return fl.Field().String() == "awesome"
}

打印出来的错误信息是:

Key: 'MyStruct.String' Error:Field validation for 'String' failed on the 'is-awesome' tag

使用checker

package main

import (
    "fmt"

    "github.com/liangyaopei/checker"
)

type MyStruct struct {
    String string
}

type isAwesomeRule struct {
    FieldExpr string
}

func (r isAwesomeRule) Check(param interface{}) (bool, string) {
    exprValue, _ := checker.FetchFieldInStruct(param, r.FieldExpr)
    exprStr := exprValue.(string)
    if exprStr != "awesome" {
        return false, fmt.Sprintf("'%s' has worng value", r.FieldExpr)
    }
    return true, ""
}

func main() {
    s := MyStruct{String: "not awesome"}
    ch := checker.NewChecker()
    rule := isAwesomeRule{FieldExpr: "String"}
    ch.Add(rule, "value is not awesome")

    isValid, prompt, errMsg := ch.Check(s)
    if !isValid {
        fmt.Printf("prompt:%s,errMsg:%s", prompt, errMsg)
    }
}

使用checker,不需要在结构体上添加校验标签,逻辑更加清晰。更多自定义规则的例子在这里。

定制错误信息

使用validator

validatortranslator
import (
    "fmt"

    "github.com/go-playground/locales/en"
    ut "github.com/go-playground/universal-translator"
    "github.com/go-playground/validator/v10"
    en_translations "github.com/go-playground/validator/v10/translations/en"
)

var (
    uni      *ut.UniversalTranslator
    validate *validator.Validate
)

func main() {

    // NOTE: ommitting allot of error checking for brevity

    en := en.New()
    uni = ut.New(en, en)

    // this is usually know or extracted from http 'Accept-Language' header
    // also see uni.FindTranslator(...)
    trans, _ := uni.GetTranslator("en")

    validate = validator.New()
    en_translations.RegisterDefaultTranslations(validate, trans)

    translateOverride(trans)
}

func translateOverride(trans ut.Translator) {

    validate.RegisterTranslation("required", trans, func(ut ut.Translator) error {
        return ut.Add("required", "{0} must have a value!", true) // see universal-translator for details
    }, func(ut ut.Translator, fe validator.FieldError) string {
        t, _ := ut.T("required", fe.Field())

        return t
    })
    ....
}

使用checker

checkerprompt
func (c *ruleChecker) Add(rule Rule, prompt string) {
    c.rules = append(c.rules, rule)
    c.prompts = append(c.prompts, prompt)
}
value is not awesome
ch.Add(rule, "value is not awesome")
isValid, prompt, errMsg := ch.Check(s)

checker易做,validator难做

validator

更改第三方包结构体的校验规则

ParamParam18<=age<=80
package thrid_party

type Param struct{
  Age `validate:"min=18,max=80"`
}
validatorthird_partyParam
package main

func validate(p thrid_party.Param)(isValid bool){
  ....
}
checker
rule := checker.NewRangeRuleInt("Age", 20, 80)
checker.Add(rule, "invlaid age")
checker

自引用的结构体校验

假设需要校验链表的长度,完整的例子在这里

type list struct {
    Name *string
    Next *list `validate:"nonzero"`
}
Nextvalidator
checker
name := "list"
    node1 := list{Name: &name, Next: nil}
    lists := list{Name: &name, Next: &node1}

    listChecker := checker.NewChecker()
    nameRule := checker.NewLengthRule("Next.Name", 1, 20)
    listChecker.Add(nameRule, "invalid info name")
Next.Name