プレイフレームワークにおける例外とエラーの処理


最初に私の個人的なブログに投稿された記事
プログラミング時には,例外を常に考慮することが重要である.あなたのコードがどれくらいよくあっても、例外を引き起こすことができる他のライブラリのユーザー問題によって要約される無効なデータは常にありえます.また、このメカニズムを使用すると、できるだけ早く無効な流れを終了する簡単な方法です.例外は正常で、すべてのサービスで扱われるべきです.例外の場合でも、私たちは、要求が実際に処理されたということを知っているようにユーザーに応答を提供しなければなりません、しかし、何かは正しくありませんでした.本稿では、プレイフレームワークにおける例外の扱い方とクライアントに対する対応方法について説明します.
https://petrepopescu.tech/2021/03/handling-exceptions-and-errors-in-play-framework/
HttpParrorHandler
Compact Frameworkは、すべての例外ハンドラーによって実装されなければならないHttpPerrorHandlerインターフェイスを提供します.このインターフェイスには2つのメソッドがあり、両方とも適切に実装する必要があります.クライアントからのエラー(無効なエンドポイントのようにアクセスされます)、一方でサーバー側で起こった例外を処理します.
public class ErrorHandler implements HttpErrorHandler {
    @Override
    public CompletionStage<Result> onClientError(Http.RequestHeader request, int statusCode, String message) {
        // Implementation
    }

    @Override
    public CompletionStage<Result> onServerError(Http.RequestHeader request, Throwable exception) {
        // Implementation
    }
}
クライアントエラーの場合は、ステータスコードとエラーメッセージを保持するJSON文字列を返します.OnClientErrorメソッドは必要な情報をすべて提供し、結果としてCompleteBrefutureを返すことができます.
public class ErrorHandler implements HttpErrorHandler {
    @Override
    public CompletionStage<Result> onClientError(Http.RequestHeader request, int statusCode, String message) {
        Map<String, Object> response = new HashMap<>();
        response.put("status", statusCode);
        response.put("error", message);
        return CompletableFuture.completedFuture(
                Results.status(statusCode, Json.toJson(response))
        );
    }

    @Override
    public CompletionStage<Result> onServerError(Http.RequestHeader request, Throwable exception) {
        return null;
    }
}
サーバーエラーは、大部分の魔法が起こるところです.我々のコードに巻き込まれていないすべての例外は、最終的にこのメソッドに到達するので、ここで適切に扱うことが重要です.例外をログバックし、適切な応答をクライアントに返す必要があります.前のメソッドのように、JSON文字列を返します.しかし、この文字列がどのように取得されるかは、もう少し複雑になります.
通常、必要な情報を保持する1つ以上のカスタム例外を定義します.この例では、サービスの特定の部分にスローされるPlatformExceptionを作成します.これにより、処理が不要になったこと、およびクライアントに応答を返すことができます.
public class PlatformException extends Exception {
    private String message;
    private String errorCode;

    public PlatformException(String message, String errorCode) {
        this.message = message;
        this.errorCode = errorCode;
    }

    public PlatformException(String message, String errorCode, Throwable throwable) {
        super(throwable);
        this.message = message;
        this.errorCode = errorCode;
    }

    // Getters
}
さて、エラーハンドラで、この例外が発生したときに、このエラーから得られたJSON文字列で500応答を返します.それが我々のカスタム例外でないならば、我々はメッセージを標準的なフォーマットですべての時間に返すのをより簡単にするために、1を造ります.
public class ErrorHandler implements HttpErrorHandler {
    private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    @Override
    public CompletionStage<Result> onClientError(Http.RequestHeader request, int statusCode, String message) {
        Map<String, Object> response = new HashMap<>();
        response.put("status", statusCode);
        response.put("error", message);
        return CompletableFuture.completedFuture(
                Results.status(statusCode, Json.toJson(response))
        );
    }

    @Override
    public CompletionStage<Result> onServerError(Http.RequestHeader request, Throwable exception) {
        LOGGER.error("Error encountered.", exception);
        Throwable exceptionToReturn = exception;
        if (!(exception instanceof PlatformException)) {
            exceptionToReturn = new PlatformException(exception.getMessage(), "UNKNOWN", exception);
        }
        return CompletableFuture.completedFuture(
                Results.internalServerError(Json.toJson(exceptionToReturn))
        );
    }
}
最後には、少なくとも、我々はエラーハンドラについての再生を通知する必要があります.これを行うには、アプリケーションを編集します.confファイルを再生します.HTTPクラス名にerrorhandlerの設定変数を設定します.
play.http.errorHandler = "ErrorHandler"

何!完全なスタックトレースが返されますか?
このコードを実行する場合は、JSONが完全なトレーストレースを含むことがわかります.これはあまりにも多くの情報を提供できるので望ましくない.すべてを知っているクライアントを必要としないので、コードを改善しましょう.第一に、我々はJSONを例外そのものから除外しない.代わりに、メッセージを作成し、エラーメッセージとエラーコードと他の情報が必要です.さて、例外が発生すると、必要最小限の情報だけが返されます.
public class ErrorHandler implements HttpErrorHandler {
    private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    @Override
    public CompletionStage<Result> onClientError(Http.RequestHeader request, int statusCode, String message) {
        Map<String, Object> response = new HashMap<>();
        response.put("status", statusCode);
        response.put("error", message);
        return CompletableFuture.completedFuture(
                Results.status(statusCode, Json.toJson(response))
        );
    }

    @Override
    public CompletionStage<Result> onServerError(Http.RequestHeader request, Throwable exception) {
        LOGGER.error("Error encountered.", exception);
        ErrorDTO error;
        if (exception instanceof PlatformException) {
           errorDTO = new ErrorDTO(exception.getMessage(), ((PlatformException) exception).getErrorCode());
        } else {
            errorDTO = new ErrorDTO(exception.getMessage(), "UNKNOWN");
        }
        return CompletableFuture.completedFuture(
                Results.internalServerError(Json.toJson(errorDTO))
        );
    }
}
最初に私の個人的なブログに投稿された記事