Skip to content

独自エラーを作成 ​

独自のエラー構造体を作成し、業務処理でエラーが発生した際にハンドリングしてjsonレスポンスを返す実装のサンプルです。

実装例 ​

internal/apperrors ​

apperrorsパッケージに下記要領で独自エラーを作成します:

  • business_error.go
go
package apperrors

// 業務エラー向け構造体です。想定内のエラーが発生した場合に返るエラーです。
type BusinessError struct {
	message string // エラーメッセージ
}

func (e BusinessError) Error() string {
	return e.message
}

func NewBusinessError(message string) BusinessError {
	return BusinessError{message: message}
}

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.BusinessError:
		return http.StatusUnprocessableEntity, businessErrorResponse{Message: e.Error()}
	default:
		return http.StatusInternalServerError, unknownErrorResponse{Message: "unknown error"}
	}
}

// 業務エラーレスポンスモデルです。想定内のエラーが発生した場合に返るエラーです。
type businessErrorResponse struct {
	Message string `json:"message"` // エラーメッセージ
}

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

internal/application/usecase ​

usecaseパッケージにて、下記要領でエラーをhandlerに返します:

  • user_usecase.go
go
package usecase

import (
	"context"
	"easyapp/internal/apperrors"
	"easyapp/internal/application/usecase/params"
	"easyapp/internal/domain"
)

// 認証のusecaseインターフェースです。
type UserUsecase interface {

	// 認証処理を行います。
	Login(ctx context.Context, in params.LoginInput) (params.LoginOutput, error)
}

type userUsecase struct {
	userRepository domain.UserRepository
}

func NewUserUsecase(userRepository domain.UserRepository) UserUsecase {
	return &userUsecase{userRepository: userRepository}
}

func (u *userUsecase) Login(ctx context.Context, in params.LoginInput) (params.LoginOutput, error) {

	name, err := domain.NewName(in.Name())
	if err != nil {
		return params.LoginOutput{}, apperrors.NewBusinessError("invalid input")
	}

	user, err := u.userRepository.FindByName(ctx, name)
	if err != nil {
		return params.LoginOutput{}, apperrors.NewBusinessError("no such user")
	}

	return params.NewLoginOutput(user.VerifyPassword(in.Password())), nil
}

internal/presentation/handler ​

handlerパッケージにて、下記要領でエラーハンドリングを行います:

  • user_handler.go
go
package handler

import (
	"easyapp/internal/application/usecase"
	"easyapp/internal/application/usecase/params"
	"easyapp/internal/presentation/handler/httperror"
	"easyapp/internal/presentation/handler/model"
	"encoding/json"
	"net/http"
)
// 認証のhandlerです。
type UserHandler struct {
	userUsecase usecase.UserUsecase
}

func NewUserHandler(userUsecase usecase.UserUsecase) UserHandler {
	return UserHandler{userUsecase: userUsecase}
}

func (h *UserHandler) Login(w http.ResponseWriter, r *http.Request) {

	req, _ := model.NewLoginRequest(r)

	out, err := h.userUsecase.Login(r.Context(), params.NewLoginInput(req.Name, req.Password))
	if err != nil {
		httpStatus, res := httperror.ToHttpErrorResponse(err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(httpStatus)
		json.NewEncoder(w).Encode(res)
		return
	}

	res := model.NewLoginResponse(out.Valid())
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(res)
}