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 に返します:

auth_usecase.go

package usecase

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

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

    // 認証処理を行います。
    Login(in payload.LoginIn) (payload.LoginOut, error)
}

type authUsecase struct {
    authRepository domain.UsersRepository
}

func NewAuthUsecase(authRepository domain.UsersRepository) AuthUsecase {
    return &authUsecase{authRepository: authRepository}
}

func (u *authUsecase) Login(in payload.LoginIn) (payload.LoginOut, error) {

    user, err := u.authRepository.FindByName(in.Name())
    if err != nil { // repositoryでエラーが発生したらEasyappErrorを戻す
        return payload.NewLoginOut(false), apperrors.NewSampleError(err.Error())
    }
    return payload.NewLoginOut(user.Password() == in.Password()), nil
}

internal/handler

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

auth_handler.go

package handler

import (
    "easyapp/internal/apperrors"
    "easyapp/internal/handler/model"
    "easyapp/internal/usecase"
    "easyapp/internal/usecase/payload"
    "encoding/json"
    "net/http"
)

// 認証のハンドラインターフェースです。
type AuthHandler interface {

    // 認証処理を呼び出します。
    Login(w http.ResponseWriter, r *http.Request)
}

type authHandler struct {
    authUsecase usecase.AuthUsecase
}

func NewAuthHandler(authUsecase usecase.AuthUsecase) AuthHandler {
    return &authHandler{authUsecase: authUsecase}
}

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

    req := model.NewLoginReq(r)

    out, err := h.authUsecase.Login(payload.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)
}