Skip to content

独自エラーを作成

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

実装例

internal/apperrors

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

base.go

独自エラーのインターフェースおよびエラーハンドリング向け関数を宣言します。各種独自例外はこのインターフェースを実装する形で作成し、HandleError関数経由でレスポンスを作成します。

package apperrors

import "net/http"

// エラーのインターフェースです。
type EasyappError interface {

    // エラーメッセージを管理します。
    Error() string

    // エラー向けのレスポンスモデルを作成します。
    ReturnError(w http.ResponseWriter)
}

// エラーのハンドリングを行います。
// EasyappErrorを実装したエラーであればそのハンドリング処理を呼び出し、それ以外は500エラーを返します。
func HandleError(w http.ResponseWriter, err error) {

    if e, ok := err.(EasyappError); ok {
        e.ReturnError(w)
        return
    }

    http.Error(w, "Internal server error", http.StatusInternalServerError)
}

sample_error.go

独自エラーの実装です。ReturnErrorでレスポンスモデルの作成処理を行っています。

package apperrors

import (
    "encoding/json"
    "net/http"
)

// サンプルのエラーです。
type SampleError struct {
    message string // エラーメッセージ
}

func NewSampleError(message string) EasyappError {
    return &SampleError{message: message}
}

func (e *SampleError) Error() string {
    return e.message
}

func (e *SampleError) ReturnError(w http.ResponseWriter) {

    res := sampleErrorRes{Message: e.message}
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusInternalServerError)
    json.NewEncoder(w).Encode(res)
}

// サンプルエラーのレスポンスモデルです。
type sampleErrorRes struct {
    Message string `json:"message"` // エラーメッセージ
}

internal/usecase

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

user_usecase.go

package usecase

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

// 認証の業務処理インターフェースです。
type UserUsecase interface {

    // 認証処理を行います。
    Login(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(in params.LoginIn) (params.LoginOut, error) {

    user, err := u.userRepository.FindByName(in.Name())
    if err != nil {
        return params.NewLoginOut(false), apperrors.NewSampleError(err.Error())
    }
    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"
)

// 認証のハンドラインターフェースです。
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(params.NewLoginIn(req.Name, req.Password))
    if err != nil { // EasyappErrorが返ってきたらハンドリング
        apperrors.HandleError(w, err)
        return
    }

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