Skip to content

バリデーション実装

リクエストパラメータのバリデーションを実装します。

cf.

事前準備

  • validatorパッケージをインストールします:
shell
go get github.com/go-playground/validator/v10

実装

internal/validation

バリデータを実装します。NewValidationErrorにてバリデーションエラーからエラーメッセージを作成しerror型として返します。

  • validator.go
go
package validation

import (
	"fmt"

	"github.com/go-playground/validator/v10"
)

type CustomValidator struct {
	validator *validator.Validate
}

func NewCustomValidator() *CustomValidator {
	return &CustomValidator{validator: validator.New()}
}

func (cv *CustomValidator) Validate(i any) error {
	if err := cv.validator.Struct(i); err != nil {
		return err
	}
	return nil
}

type ValidationError struct {
	Messages []string `json:"messages" example:"invalid request"`
}

func (e ValidationError) Error() string {
	return fmt.Sprintf("%v", e.Messages)
}

func NewValidationError(err error) ValidationError {
	validationErrors, ok := err.(validator.ValidationErrors)
	if !ok {
		return ValidationError{[]string{"unknown error"}}
	}
	var errorMessages []string
	for _, e := range validationErrors {
		errorMessages = append(
			errorMessages,
			fmt.Sprintf("invalid parameter. field: %s, tag: %s, value: %v",
				e.Field(),
				e.Tag(),
				e.Value(),
			),
		)
	}

	return ValidationError{Messages: errorMessages}
}

internal/bootstrap/

  • server.go

バリデータを初期化するため下記を宣言します:

go
	e := echo.New()
	e.Validator = validation.NewCustomValidator()

internal/presentation/handler/model

validateタグを用いてバリデーションを宣言し、c.Validateで値の検証を行います。

  • user_model.go
go
type LoginRequest struct {
	Name     string `json:"name" example:"nob"`                            // ユーザ名
	Password string `json:"password" example:"passwd" validate:"required"` // パスワード
}

func NewLoginRequest(c *echo.Context) (LoginRequest, error) {

	req := new(LoginRequest)
	if err := c.Bind(req); err != nil {
		return LoginRequest{}, errors.New("invalid request")
	}
	if err := c.Validate(req); err != nil {
		return LoginRequest{}, validation.NewValidationError(err)
	}

	return *req, nil
}

internal/presentation/handler

NewLoginRequestからバリデーションエラーが返ってきた場合、そのエラーをAPIレスポンスとして返します。

  • user_handler.go
go
	req, err := model.NewLoginRequest(c)
	if err != nil {
		if err, ok := err.(validation.ValidationError); ok {
			return c.JSON(
				http.StatusUnprocessableEntity,
				err,
			)
		}
		return c.JSON(
			http.StatusBadRequest,
			echo.NewHTTPError(
				http.StatusBadRequest,
				"bad request",
			),
		)
	}