Skip to content

カスタムチェッカー

組み込みでカバーされていない要件があるなら、関数を govalid.Checkers map に追加するだけです。一度登録すれば、新ルール は任意の valid タグから利用できます——他の配線は不要です。

一番シンプルな例

go
package main

import (
    "fmt"
    "strings"

    "github.com/wuhan005/govalid"
)

func main() {
    govalid.SetMessageTemplates(map[string]string{
        "noE99": "can not contain 'e99'",
    })

    govalid.Checkers["noE99"] = func(c govalid.CheckerContext) *govalid.ErrContext {
        v, ok := c.FieldValue.(string)
        if !ok {
            return govalid.MakeValueTypeError(c)
        }
        if strings.Contains(v, "e99") {
            return govalid.NewErrorContext(c)
        }
        return nil
    }

    r := struct {
        Content string `valid:"noE99" label:"内容"`
    }{Content: "helloe99"}

    if errs, ok := govalid.Check(r); !ok {
        for _, err := range errs {
            fmt.Println(err)
        }
    }
}
text
内容can not contain 'e99'

仕様

チェッカーとは:

go
type CheckFunc func(ctx CheckerContext) *ErrContext

成功時に nil、失敗時に非 nil の *ErrContext を返します。

CheckerContext には必要なものが全部入っています:

フィールド何が得られるか
FieldValue実際の値(多くの場合は型アサーション)
FieldTypeフィールドの reflect.Type
FieldNameGo フィールド名
FieldLabel解決済みラベル(ロケール対応)
StructValue親構造体の reflect.Value(クロスフィールド用)
Rule.paramsタグからのパラメータ、例:list:a,b,c["a","b","c"]
TemplateLanguageアクティブなロケール

エラーヘルパー

*ErrContext を手で作らないでください。ヘルパーを使います:

ヘルパー使い時
NewErrorContext(c)標準のルール失敗——登録済みテンプレートを使う
MakeValueTypeError(c)フィールドの実行時種別がサポート外
MakeCheckerParamError(c)タグパラメータが欠落・不正
MakeFieldNotFoundError(c)クロスフィールドルールが未知のフィールドを参照

パラメータの利用

go
const checker = "startsWith"

govalid.SetMessageTemplates(map[string]string{
    checker: "必须以指定前缀开头",
})

govalid.Checkers[checker] = func(c govalid.CheckerContext) *govalid.ErrContext {
    if len(c.Rule.params) == 0 {
        return govalid.MakeCheckerParamError(c)
    }
    v, ok := c.FieldValue.(string)
    if !ok {
        return govalid.MakeValueTypeError(c)
    }
    for _, p := range c.Rule.params {
        if strings.HasPrefix(v, p) {
            return nil
        }
    }
    return govalid.NewErrorContext(c)
}
go
type Form struct {
    Path string `valid:"startsWith:/api,/admin"`
}

兄弟フィールドを見る

c.StructValue で他のフィールドを名前で読み取ります。組み込みの equal チェッカーが良いお手本です:

go
govalid.Checkers["different"] = func(c govalid.CheckerContext) *govalid.ErrContext {
    if len(c.Rule.params) != 1 {
        return govalid.MakeCheckerParamError(c)
    }
    if !c.StructValue.IsValid() || c.StructValue.Kind() != reflect.Struct {
        return govalid.MakeFieldNotFoundError(c)
    }

    other := c.Rule.params[0]
    structType := c.StructValue.Type()
    for i := 0; i < structType.NumField(); i++ {
        if structType.Field(i).Name == other {
            if fmt.Sprintf("%v", c.FieldValue) == fmt.Sprintf("%v", c.StructValue.Field(i).Interface()) {
                return govalid.NewErrorContext(c)
            }
            return nil
        }
    }
    return govalid.MakeFieldNotFoundError(c)
}

ローカライズされたメッセージを提供する

組み込みチェッカーは各ロケールテンプレートにエントリを持ちます。 サポートしたい各ロケールに対して SetMessageTemplates で追加します:

go
govalid.SetMessageTemplates(map[string]string{
    "noE99": "can not contain 'e99'",
}, language.English)

govalid.SetMessageTemplates(map[string]string{
    "noE99": "不能包含 e99",
}, language.Chinese)

TIP

テンプレート文字列はフィールドラベルと連結されます。 {{ で始まり }} で終わる テンプレートはラベルを完全にスキップします——_unknownErrorTemplate のようなシステム系メッセージに有用です。

起動時に登録する

Checkers map は並行書き込みに対して 同期されていません。 プログラム起動時に一度書き換え、リクエストが流れ始めたら触らない でください:

go
func init() {
    govalid.Checkers["noE99"] = noE99Checker
}

書き手がいない限り map の読み込みは並行安全で、これは典型的な Go map の契約です。

MIT ライセンスのもとで公開されています。