この投稿で使用している言語、ライブラリのバージョン
- Go v1.17.7
- github.com/go-playground/validator v10.10.0
疑問に思ったこと
validatorのタグには、gtecsfield
とgtefield
や、ltcsfield
とltfield
といったcs
付きとそうでないものがあります。
この違いは何か?
例えば gtecsfield
/gtefield
は以下のような説明があります。
Tag | Description |
---|---|
gtecsfield | Field Greater Than or Equal To Another Relative Field |
gtefield | Field Greater Than or Equal To Another Field |
この説明を読んで、ある構造体Aが別の構造体Bを持っていて、AのフィールドとBのフィールドとを比較したい場合に *csfieldタグ を使用するのかな?と思っていました。
例えばこんな感じ
type Event struct {
Name string
StartDate time.Time `validate:"gtecsfield=Notification.NotificationTime"`
Notification Notification
}
type Notification struct {
NotificationTime time.Time
}
でもこのコードはStartDate
のタグをgtecsfield
からgtefield
に変更しても問題なく動きます。
package main
import (
"fmt"
"time"
"github.com/go-playground/validator/v10"
)
type Event struct {
Name string
StartDate time.Time `validate:"gtefield=Notification.NotificationTime"` // <= `gtecsfield`ではなく`gtefield`を使う
Notification Notification
}
type Notification struct {
NotificationTime time.Time
}
func main() {
validate := validator.New()
now := time.Now()
validEvent := Event{
Name: "EventName1",
StartDate: now,
Notification: Notification{
NotificationTime: now.AddDate(0, 0, -1),
},
}
if err := validate.Struct(validEvent); err != nil {
errs := err.(validator.ValidationErrors)
for _, errrs := range errs {
fmt.Printf("validEvent errs=%+v\n", errrs)
}
}
invalidEvent := Event{
Name: "EventName1",
StartDate: now,
Notification: Notification{
NotificationTime: now.AddDate(0, 0, 1),
},
}
if err := validate.Struct(invalidEvent); err != nil {
errs := err.(validator.ValidationErrors)
for _, errrs := range errs {
fmt.Printf("invalidEvent errs=%+v\n", errrs)
}
}
}
$ go run main.go
invalidEvent errs=Key: 'Event.StartDate' Error:Field validation for 'StartDate' failed on the 'gtefield' tag
じゃあ *csfield
ってどこで使うの?
該当箇所のソースコード
gtecsfield
とgtefield
のそれぞれのタグを使用した場合に呼ばれる関数はそれぞれ以下の通りです。
ローカル変数が若干異なることを除き、違いは一箇所だけで *csfield
は Array, Slice, Map の場合にlenで比較しています。
gtecsfieldタグはArray, Slice, Mapの場合lenで比較している
gtefieldタグはArray, Slice, Mapの判定をしていない
試してみると確かにgtefield
ではSliceのサイズでのバリデーションができませんでした。
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type Hoge struct {
Items1 []string `validate:"gtecsfield=Items2"`
Items2 []string
}
type Fuga struct {
Items1 []string `validate:"gtefield=Items2"`
Items2 []string
}
func main() {
validate := validator.New()
hoge := Hoge{
Items1: []string{"A", "B"},
Items2: []string{"A", "B", "C"},
}
if err := validate.Struct(hoge); err != nil {
errs := err.(validator.ValidationErrors)
for _, errrs := range errs {
fmt.Printf("hoge errs=%+v\n", errrs)
}
}
fuga := Fuga{
Items1: []string{"A", "B"},
Items2: []string{"A", "B", "C"},
}
if err := validate.Struct(fuga); err != nil {
errs := err.(validator.ValidationErrors)
for _, errrs := range errs {
fmt.Printf("fuga errs=%+v\n", errrs) // Items2でバリデーション違反になってほしいがならない
}
}
}
$ go run main.go
hoge errs=Key: 'Hoge.Items1' Error:Field validation for 'Items1' failed on the 'gtecsfield' tag
まとめ
gtecsfield
やltcsfield
といったcs
付きのタグは Array, Slice, Map の場合にlenを見てくれるのが違い。
基本的には cs
付きのタグを使っておけば良さそう。