Appearance
独自エラーを作成
独自のエラー構造体を作成し、業務処理でエラーが発生した際にハンドリングして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)
}