Skip to content

独自エラーを作成

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