Skip to content

バリデーション実装 ​

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

cf.

事前準備 ​

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

実装 ​

internal/validation ​

バリデータを初期化します。

  • validator.go
go
package validation

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

var Validate = validator.New()

internal/apperrors ​

エラーの構造体の定義およびバリデーションエラーのハンドリングを実装します。

  • validation_error.go
go
package apperrors

import (
	"fmt"

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

// バリデーションエラー向けの構造体です。
type ValidationError struct {
	messages []string
}

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}
}

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

func (e ValidationError) Errors() []string {
	return e.messages
}

internal/presentation/handler/httperror ​

エラーハンドリングを行いHTTPレスポンスを返します。

  • httperror.go
go
package httperror

import (
	"easyapp/internal/apperrors"
	"net/http"
)

// エラー型に対し、そのHTTPステータスおよびレスポンス構造体を返します。
func ToHttpErrorResponse(err error) (int, any) {

	switch e := err.(type) {
	case apperrors.ValidationError:
		return http.StatusUnprocessableEntity, validationErrorResponse{Messages: e.Errors()}
	default:
		return http.StatusInternalServerError, unknownErrorResponse{Message: "unknown error"}
	}
}

// バリデーションエラーレスポンスモデルです。
type validationErrorResponse struct {
	Messages []string `json:"messages"` // エラーメッセージ
}

// 未知のエラーのレスポンスです。
type unknownErrorResponse struct {
	Message string `json:"message"` // エラーメッセージ
}

internal/presentation/handler/model ​

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

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

func NewLoginRequest(r *http.Request) (loginRequest, error) {

	var req loginRequest
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&req); err != nil {
		return loginRequest{}, err
	}
	if err := validation.Validate.Struct(req); err != nil {
		return loginRequest{}, apperrors.NewValidationError(err)
	}

	return req, nil
}

internal/presentation/handler ​

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

  • user_handler.go
go
	req, err := model.NewLoginRequest(r)
	if err != nil {
		httpStatus, res := httperror.ToHttpErrorResponse(err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(httpStatus)
		json.NewEncoder(w).Encode(res)
		return
	}