🦔
Goの`int` で `0` を送ると バリデーション`required` に引っかかる件
今回は Gin(Go)で API バリデーションをしていて、int
型のフィールドに 0
を送ったら required
バリデーションに引っかかるという現象に遭遇したので、記録として残します。
結論
問題内容
int 型の 0 が required に引っかかる
解決
*int(ポインタ型)に変更する
問題の発端
とある構造体で以下のようなバリデーションを定義していました。
バックエンド側でのDTOの定義はこれ。
type UpdateCounterDTO struct {
CounterName string `json:"counter_name" binding:"max=300"`
Count int `json:"count" binding:"required,min=0,max=10000"`
}
実際のクライアント側でのリクエストのペイロードは以下。
{count: 0}
そして、リクエストのレスポンスは以下。
{
"error": "bad_request",
"message": "Bad requestKey: 'UpdateCounterDTO.Count' Error:Field validation for 'Count' failed on the 'required' tag"
}
なぜでしょうか?
countで0を送ってるのに required に引っかかってる!?
(ginさん、0は0でnilじゃないんだよ!!)
原因
Goでは、int のゼロ値は 0です。
そして Gin のバリデーションライブラリ(go-playground/validator)は、required の評価にゼロ値かどうかを使っています。
つまり int 型のフィールドに 0 を入れても、「値が設定されていない(=ゼロ値)」と見なされてしまいます。
値 Goの評価 requiredの評価
0 正常な int 値 ゼロ値なので「未設定」とみなされる
解決法:ポインタ型(*int)を使う
構造体を以下のように修正します。
type UpdateCounterDTO struct {
CounterName string `json:"counter_name" binding:"max=300"`
Count *int `json:"count" binding:"required,min=0,max=10000"`
}
こうすることで、nil(値が送られていない)と 0(明示的に送られた)を区別できるようになり、required バリデーションが正しく動作します。
補足:なぜポインタだと通るのか
ポインタ型にすることで、以下のような動作になります。
ペイロード | Goでの値 | required の評価 |
---|---|---|
{} | Count = nil(全機能の上位概念) | 弾かれる(未設定) |
{"count": 0} | Count = &0 | 通る(明示的な 0) |
同じことでハマる人、きっと多いはずなので、どなたかの助けになれば幸いです。
Discussion