独自エラーを作成
独自のエラー構造体を作成し、業務処理でエラーが発生した際にハンドリングしてjsonレスポンスを返す実装のサンプルです。
実装例
internal/apperrors
apperrorsパッケージに下記要領で独自エラーを作成します:
easyapp_business_error.go
独自エラーの実装です。エラーの構造体およびHTTPレスポンス向けの構造体を定義します:
package apperrors
// easyappの業務エラー向け構造体です。想定内のエラーが発生した場合に返るエラーです。
type EasyappBusinessError struct {
message string // エラーメッセージ
}
func NewEasyappBusinessError(message string) EasyappBusinessError {
return EasyappBusinessError{message: message}
}
func (e EasyappBusinessError) Error() string {
return e.message
}
// easyappの業務エラーレスポンスモデルです。想定内のエラーが発生した場合に返るエラーです。
type easyappBusinessErrorRes struct {
Message string `json:"message"` // エラーメッセージ
}
base.go
独自エラーがusecaseから投げられた場合のHTTPレスポンスを定義します:
package apperrors
import (
"net/http"
)
// エラー型に対し、そのHTTPステータスおよびレスポンス構造体を返します。
func ToHttpErrorRes(err error) (int, any) {
switch e := err.(type) {
case EasyappBusinessError:
return http.StatusUnprocessableEntity, easyappBusinessErrorRes{Message: e.message}
default:
return http.StatusInternalServerError, struct{ Message string }{Message: "unknown error"}
}
}
internal/usecase
usecaseパッケージにて、下記要領でエラーをhandlerに返します:
user_usecase.go
package usecase
import (
"context"
"easyapp/internal/apperrors"
"easyapp/internal/domain"
"easyapp/internal/usecase/params"
)
// 認証のusecaseインターフェースです。
type UserUsecase interface {
// 認証処理を行います。
Login(ctx context.Context, in params.LoginIn) (params.LoginOut, 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.LoginIn) (params.LoginOut, error) {
user := u.userRepository.FindByName(ctx, domain.NewFindByNameQuery(in.Name()))
if user.Name() == "" {
return *new(params.LoginOut), apperrors.NewEasyappBusinessError("user not found")
}
return params.NewLoginOut(user.Password() == in.Password()), nil
}
internal/handler
handlerパッケージにて、下記要領でエラーハンドリングを行います:
user_handler.go
package handler
import (
"easyapp/internal/apperrors"
"easyapp/internal/handler/model"
"easyapp/internal/usecase"
"easyapp/internal/usecase/params"
"encoding/json"
"net/http"
)
// 認証のhandlerインターフェースです。
type UserHandler interface {
// 認証処理を呼び出します。
Login(w http.ResponseWriter, r *http.Request)
}
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.NewLoginReq(r)
out, err := h.userUsecase.Login(r.Context(), params.NewLoginIn(req.Name, req.Password))
if err != nil {
status, res := apperrors.ToHttpErrorRes(err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(res)
return
}
res := model.NewLoginRes(out.Valid())
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(res)
}