【Golang】echoとvalidator.v9を使ったカスタムバリデーションのサンプル

Golangの[echo](https://github.com/labstack/echo)と[validator.v9](https://github.com/go-playground/validator)を使って、独自のバリデータタグを定義できるまでの手順の紹介.
# echoでカスタムバリデータを扱う
[公式ページ](https://echo.labstack.com/guide/request)を参考にしつつ、echo.Validatorにカスタムバリデータを設定する.　　
まずはカスタムバリデータを作成する.

```go
// CustomValidator
type CustomValidator struct {
	validator *validator.Validate
}

// NewValidator
func NewValidator() echo.Validator {
	return &CustomValidator{validator: validator.New()}
}

// Validate validate
func (cv *CustomValidator) Validate(i interface{}) error {
	return cv.validator.Struct(i)
}
```

作成したバリデータをe.Validatorに設定.

```main.go
func main() {
	e := echo.New()
	e.Validator = NewValidator()
	e.GET("/", func(c echo.Context) (err error) {
		u := new(User)
		if err = c.Bind(u); err != nil {
			return
		}
		if err = c.Validate(u); err != nil {
			return
		}
		return c.JSON(http.StatusOK, u)
	})
	e.Logger.Fatal(e.Start(":1323"))
}
```

これでvalidator.v9が使えるようになった.

# バリデーションを実施する
validator.v9でバリデーションを実施してみる.
例えば`FooStruct`を定義して、`validate`項目に`requied`タグを設定すると必須になる.
他にも色々なタグがあって便利なので、[公式ページ](https://godoc.org/gopkg.in/go-playground/validator.v9)をみて自分の用途にあったバリデーションを設定して使う.

```go
// Foo struct
type Foo struct {
	Bar string `query:"bar" validate:"requied"`
}

func main() {
	e := echo.New()
	e.Validator = NewValidator()
	e.GET("/", func(c echo.Context) (err error) {
		u := new(Foo)
		if err = c.Bind(u); err != nil {
			rreturn echo.NewHTTPError(http.StatusBadRequest, err.Error())
		}
		if err = c.Validate(u); err != nil {
			return echo.NewHTTPError(http.StatusBadRequest, err.(validator.ValidationErrors).Error())
		}
		return c.JSON(http.StatusOK, u)
	})
	e.Logger.Fatal(e.Start(":1323"))
}
```

これで`requied`バリデーションが設定され,クエリパラメータにbar項目を含めないとバリデーションに失敗するようになった.

```cmd
$ curl -X GET "http://127.0.0.1:1322"
{"message":"Key: 'Foo.Bar' Error:Field validation for 'Bar' failed on the 'required' tag"}

$ curl -X GET "http://127.0.0.1:1322/?bar=hoge"
{"Bar":"hoge"}
```

# 独自のカスタムバリデーションを定義する
独自のバリデーションタグを作成したい場合は、 `validator.RegisterValidation()`を利用する.
`validator.RegisterValidation()`を設定するためにまずはboolを返すメソッドを定義する.
以下は文字列が`hogehoge`と一致しない場合に失敗するバリデーションの例.ここで好きに条件を定義できる.

```go
// CustomValidate
func CustomValidate(fl validator.FieldLevel) bool {
	return fl.Field().String() == "hogehoge"
}
```
バリデーションが作成できたのでカスタムバリデータに`CustomValidate()`を追加する.

```go
// CustomValidator
type CustomValidator struct {
	validator *validator.Validate
}

// NewValidator
func NewValidator() echo.Validator {
	return &CustomValidator{validator: validator.New()}
}

// Validate validate
func (cv *CustomValidator) Validate(i interface{}) error {
	cv.validator.RegisterValidation("hogehoge", CustomValidate)
	return cv.validator.Struct(i)
}
```
これで新たに`hogehoge`バリデーションタグを作ることができた.
先ほど定義した`Foo Struct`にルールを追加する.

```go
// Foo struct
type Foo struct {
	Bar string `query:"bar" validate:"requied,hogehoge"`
}
```
これで`hogehoge`ルールが追加され、独自タグでのバリデーションが行えるようになった.
今までの`required`も動作している.

```
$ curl -X GET "http://127.0.0.1:1322/?bar=hoge"
{"message":"Key: 'Foo.Bar' Error:Field validation for 'Bar' failed on the 'hogehoge' tag"}

$ curl -X GET "http://127.0.0.1:1322"
{"message":"Key: 'Foo.Bar' Error:Field validation for 'Bar' failed on the 'required' tag"}

$ curl -X GET "http://127.0.0.1:1322/?bar=hogehoge"
{"Bar":"hogehoge"}
```

こんな感じでechoとvalidator.v9を利用して、簡単にバリデーションが実施できる.
validator.v9はデフォルト状態でも簡単なアプリには必要十分なバリデーションが揃っているし、紹介した以外にも色々設定できることもあるようなのでどんどん使っていきたい.

# 全体コード
最終的なコードはこんな感じでした.

```main.go
package main

import (
	"net/http"

	"github.com/labstack/echo"
	"gopkg.in/go-playground/validator.v9"
)

// CustomValidator
type CustomValidator struct {
	validator *validator.Validate
}

// NewValidator
func NewValidator() echo.Validator {
	return &CustomValidator{validator: validator.New()}
}

// Validate validate
func (cv *CustomValidator) Validate(i interface{}) error {
	cv.validator.RegisterValidation("hogehoge", CustomValidate)
	return cv.validator.Struct(i)
}

// CustomValidate
func CustomValidate(fl validator.FieldLevel) bool {
	return fl.Field().String() == "hogehoge"
}

// Foo struct
type Foo struct {
	Bar string `query:"bar" validate:"required,hogehoge"`
}

func main() {
	e := echo.New()
	e.Validator = NewValidator()
	e.GET("/", func(c echo.Context) (err error) {
		u := new(Foo)
		if err = c.Bind(u); err != nil {
			return echo.NewHTTPError(http.StatusBadRequest, err.Error())
		}
		if err = c.Validate(u); err != nil {
			return echo.NewHTTPError(http.StatusBadRequest, err.(validator.ValidationErrors).Error())
		}
		return c.JSON(http.StatusOK, u)
	})
	e.Logger.Fatal(e.Start(":1322"))
}
```