カスタムチェッカー
組み込みでカバーされていない要件があるなら、関数を 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 |
FieldName | Go フィールド名 |
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 の契約です。