Go言語におけるバリデーションモジュールの返却値の覚え書き

Go/Echoで、APIのパラメータのバリデーションどうしようと調べて見つかったモジュールが「`gopkg.in/go-playground/validator`」。
そのリファレンスの返却値部分のメモです。

## 使用モジュール
最新のメジャーバージョンはv10のようなのですが、gomodのプロキシ周りの設定？が怪しいのかリンクエラーが出まくってしまったため、本例ではv9を使っています。
（本当はgithub.comだけどgopkg.in使えとエラーが出てるがVSCodeだと補完できずエラーになる）

- v9リファレンス<br>
https://pkg.go.dev/gopkg.in/go-playground/validator.v9


## 概要

1. チェックしたい構造体にバリデーションルールを書いたタグを付与する
1. validatorインスタンスのStructメソッドに、チェックしたい構造体のインスタンスを渡す
1. 戻り値（error）がリストに入ったinterface型で返ってくるので、`validator.ValidationErrors`に型アサーションして使用する

例えばAPIの返却値としてエラー内容を構造化してクライアントに返すためには、3の`validator.ValidationErrors`下のメソッドを使う必要があります。

## サンプル
<details><summary>コピペでPlaygroundで動きます。</summary>
<div>

```Golang:
package main

import (
	"fmt"

	"gopkg.in/go-playground/validator.v9"
)

type paramUpdateUserInfo struct {
	ID     int    `json:"id" validate:"required,gte=0"`
	Name   string `json:"name" validate:"required,min=10,max=100"`
	Memo   string `json:"memo"`
}

func main() {

	param := paramUpdateUserInfo{
		ID:     1,
		Name:   "あ",
	}

	if err := validator.New().Struct(param); err != nil {
		for _, desc := range err.(validator.ValidationErrors) {
			fmt.Println("Field: " + desc.Field())
			fmt.Println("StructField: " + desc.StructField())
			fmt.Println("Namespace: " + desc.Namespace())
			fmt.Println("StructNamespace: " + desc.StructNamespace())

			fmt.Println("Kind: " + desc.Kind().String()) // reflect
			fmt.Println("Type: " + desc.Type().String()) // reflect

			fmt.Println("Value: " + desc.Value().(string)) // interface

			fmt.Println("Tag: " + desc.Tag())
			fmt.Println("ActualTag: " + desc.ActualTag())
			fmt.Println("Param: " + desc.Param())
		}
	}
}
```
</div>
</details>
または https://play.golang.org/p/ibPUaf7fE_B

## メソッド

公式リファレンス
https://pkg.go.dev/gopkg.in/go-playground/validator.v9#FieldError

**サンプルの前提**
構造体名： `paramUpdateUserInfo`
validateタグ： `validate:"required,min=10,max=100"`
エラーがあるフィールド名： `Name`
エラー内容： 文字数不足エラー

|メソッド|文字列変換した場合の返却値|説明|
|------|------|------|
|Field|Name|エラーが発生したフィールド名。<br>タグがある場合はその名前が優先される。|
|StructField|Name|エラーが発生したフィールド名（構造体の定義名）。<br>|
|Namespace|paramUpdateUserInfo.Name|エラーが発生したフィールド名にネームスペースを付与したもの。<br>タグがある場合はその名前が優先される。|
|StructNamespace|paramUpdateUserInfo.Name|エラーが発生したフィールド名にネームスペースを付与したもの。|
|Kind|string|reflectのKind型が返却される。<br>Stringメソッドを使うことで、型を示す文字列を取得可能。|
|Type|string|reflectのType型が返却される。<br>Stringメソッドを使うことで、型を示す文字列を取得可能。|
|Value|あ|エラーが発生した値のinterface値。型アサーションが必要。<br>動的にKindを渡したいところですが、interfaceにreflectは渡せないらしくエラーになります。。（循環参照的な？）|
|Tag|min|validateタグで設定され、失敗した検証内容。<br>エイリアスが設定されていた場合、実際の検証内容ではなくエイリアスが返却される。<br>例えばエイリアスが"iscolor": "hexcolor&#124;rgb&#124;rgba&#124;hsl&#124;hsla"の場合、返却値はiscolor。|
|ActualTag|min|validateタグで設定され、失敗した検証内容。<br>エイリアスが設定されていた場合、エイリアス名ではなく実際の検証内容が返却される。<br>例えばエイリアスが"iscolor": "hexcolor&#124;rgb&#124;rgba&#124;hsl&#124;hsla"の場合、返却値はhexcolor&#124;rgb&#124;rgba&#124;hsl&#124;hsla。|
|Param|10|validateタグで設定され、失敗した検証のパラメータ。しきい値など。|

例えばフロントで「名前は10文字以上で設定してください」というエラーメッセージを作りたいとして、
欲しい情報は

- StructField (= Name)
- Tag (= min)
- Param (= 10)

辺りでしょうか？
半角数字のみで～とかそういうのはカスタムバリデーションで設定することになると思いますが、
この記事ではとりあえずここまで。

**参考**
https://qiita.com/RunEagler/items/ad79fc860c3689797ccc
https://qiita.com/ayatothos/items/064ce18f4260b2503249