ginのvalidatorでpanic: Bad field type **が出た時の対策

4 min read読了の目安(約4200字

TL;DR

結論は独自ルールの名前がuniqueになっていた為、でした。
なぜか上書きできておらず、uniqueルールにのっとって処理されていた為な様ですね。
ちゃんと名付けします〜〜〜〜

起こったこと

いつもの様にモデルを実装して、テストを書いて、commit前に一回テストを回して、pushしよう、そう思った時!

panic: Bad field type int [recovered]
	panic: Bad field type int

goroutine 42 [running]:
testing.tRunner.func1.2(0x74d880, 0xc00031fd60)
	/usr/local/go/src/testing/testing.go:1144 +0x332

〜〜〜〜〜 略 〜〜〜〜〜〜

	/go/src/github.com/Diwamoto/yource/model/UserModel.go:52 +0x14a
main/test/model.TestValidateUser(0xc000383200)
	/go/src/github.com/Diwamoto/yource/test/model/UserModel_test.go:98 +0x118
testing.tRunner(0xc000383200, 0x7cbba0)
	/usr/local/go/src/testing/testing.go:1194 +0xef
created by testing.(*T).Run
	/usr/local/go/src/testing/testing.go:1239 +0x2b3
FAIL	main/test/model	0.286s

なんやこれ、、、
とりあえず直そう。。。

解決していく

goはフレームワークの構造がとても簡単に作られているのでコールスタックとエラーログがマジでみやすいです。
ここではgoの開発で詰まったときに僕がいつもやるやり方で解決していきます。
大体こんな感じで進めます。

  1. エラーとコードを見て原因がわかってすぐ直せそうなら直して終わりです。
  2. それでもわからなければまずエラーコードで検索します。
  3. それでもわからなければエラーを起こしているところに潜らないとお手上げなので、まずgithubで対象レポジトリを検索します。
  4. レポジトリのページに辿りついたらレポジトリ内検索でエラー文をぐぐります。今回でいうとintの部分は動的に取ってきそうなので、Bad field typeで検索します。
  5. 出てきたらその周りから登っていくしかないです。僕の環境(vscode)ではなぜかデバッガーが動かない(動かそうとして断念した)のでそこからがんばって登っていきます。

それでは改めて調査していきましょう。
先ほど略した場所をみます。

panic(0x74d880, 0xc00031fd60)
	/usr/local/go/src/runtime/panic.go:965 +0x1b9
github.com/go-playground/validator.isUnique(0x8234f8, 0xc000370fc0, 0x7831e0)
	/go/pkg/mod/github.com/go-playground/validator@v9.31.0+incompatible/baked_in.go:260 +0xb1f
github.com/go-playground/validator.wrapFunc.func1(0x820950, 0xc00001a068, 0x8234f8, 0xc000370fc0, 0x74cf80)
	/go/pkg/mod/github.com/go-playground/validator@v9.31.0+incompatible/baked_in.go:35 +0x39
github.com/go-playground/validator.(*validate).traverseField(0xc000370fc0, 0x820950, 0xc00001a068, 0x79fb00, 0xc0000ab270, 0x99, 0x74cf80, 0xc0000ab2a8, 0x82, 0xc000463480, ...)
	/go/pkg/mod/github.com/go-playground/validator@v9.31.0+incompatible/validator.go:445 +0x570
github.com/go-playground/validator.(*validate).validateStruct(0xc000370fc0, 0x820950, 0xc00001a068, 0x79fb00, 0xc0000ab270, 0x99, 0x79fb00, 0xc0000ab270, 0x99, 0x824900, ...)
	/go/pkg/mod/github.com/go-playground/validator@v9.31.0+incompatible/validator.go:77 +0x4e9
github.com/go-playground/validator.(*validate).traverseField(0xc000370fc0, 0x820950, 0xc00001a068, 0x79a0c0, 0xc0000ab1e0, 0x99, 0x79fb00, 0xc0000ab270, 0x99, 0xc000463480, ...)
	/go/pkg/mod/github.com/go-playground/validator@v9.31.0+incompatible/validator.go:225 +0x3d2c
github.com/go-playground/validator.(*validate).validateStruct(0xc000370fc0, 0x820950, 0xc00001a068, 0x79a0c0, 0xc0000ab1e0, 0x99, 0x79a0c0, 0xc0000ab1e0, 0x99, 0x824900, ...)
	/go/pkg/mod/github.com/go-playground/validator@v9.31.0+incompatible/validator.go:77 +0x4e9
github.com/go-playground/validator.(*Validate).StructCtx(0xc00027af60, 0x820950, 0xc00001a068, 0x79a0c0, 0xc0000ab1e0, 0x0, 0x0)
	/go/pkg/mod/github.com/go-playground/validator@v9.31.0+incompatible/validator_instance.go:304 +0x38a
github.com/go-playground/validator.(*Validate).Struct(...)
	/go/pkg/mod/github.com/go-playground/validator@v9.31.0+incompatible/validator_instance.go:277

どうやらvalidatorが悪そうですね。ということでgithubのvalidatorのレポジトリに行ってレポジトリから検索してみましょう。すると出てきました。

ほーん、なるほど、対象となるファイルが出てきました。しかも下の方を見てみるとそれと同じエラーが起こるかどうかのテストまでありそうです。ライブラリとしてすばらしいですね。それでは見ていきましょう。

見ていくと、panicはisUnique()で起こっているようです。
そういえば、独自関数でユニークであるかどうかを検査するバリデーションをこう書いていました。

type User struct {
	Email    int `validate:"unique"`}

これは、、、uniqueは既に準備されてるということですかね?
このカラムがユニークかどうかはデータベースに問い合わせて確認するので、独自にバリデーションを書く必要があります。ということでuniqueではなくしてみましょう。
こんな感じにしました。

type User struct {
	Id  int `validate:"uniqueEmail"`}

in validation func
~~~~~~~~~
validate := validator.New()
validate.RegisterValidation("uniqueEmail", ValidateUniqueEmail)
~~~~~~~~~

//バリデーション用関数
func ValidateUniqueEmail(fl validator.FieldLevel) bool {
	//dbに問い合わせて既に存在していればバリデーションエラーにする

}

するときちんと動く様になりました。

原因

原因は独自バリデーションルールをuniqueという名前で作っており、そのルールは配列やスライスの中に重複したものがないかということでした。
他にも調査したところ、そもそもgo-playground/validatorはdbを扱うには不向きなようです、、、
独自ライブラリとして書き起こそうかと思います。。。