Scalaを使用したPlayFrameworkでのエラー処理
1. 概要
このチュートリアルでは、 PlayFrameworkでのデフォルトのエラー処理を見ていきます。 さらに、カスタムエラーハンドラーを実装します。
まず、さまざまなエラーを返すPlayコントローラーを定義して、エラーが自動的に処理される方法を確認します。 次に、カスタムエラーハンドラーを提供し、その制限を確認します。
2. PlayFrameworkエラー
Playコントローラーは、次の3つの場合にエラーを返します。
- 指定されたリクエストは無効であり、検証に失敗します(クライアント側のエラー)
- アプリケーションコードが例外をスローする(サーバー側エラー)
- Scalaコードでエラー応答を明示的に返します(ドメイン固有のエラー)
3. コントローラエラー
これら3つのエラーすべてを返すコントローラークラスを準備して、何が起こるかを確認し、エラーハンドラーを試してみましょう。
まず、さまざまな種類のエラーを返す5つのメソッドを使用して新しいコントローラークラスを定義します。
class ErrorDemoController @Inject()(
val controllerComponents: ControllerComponents
) extends BaseController {
def notFound(): Action[AnyContent] = Action {
NotFound
}
def exception(): Action[AnyContent] = Action {
throw new RuntimeException("Pretend that we have an application error.")
Ok // We add this line just to make the returned type match the expected type
}
def internalError(): Action[AnyContent] = Action {
InternalServerError
}
def badRequest(): Action[AnyContent] = Action {
BadRequest
}
}
コントローラを定義した後、新しいメソッドをルートハンドラとしてルートファイルに追加します。
GET /errors/notfound controllers.ErrorDemoController.notfound()
GET /errors/exception controllers.ErrorDemoController.exception()
GET /errors/internalerror controllers.ErrorDemoController.internalError()
GET /errors/badRequest controllers.ErrorDemoController.badRequest()
4. デフォルトのエラー処理
それでは、エンドポイントに1つずつアクセスして、何が起こるかを見てみましょう。 最初に、PlayFrameworkがアプリケーションエラーを処理する方法を示します。
4.1. Scala例外の処理
ブラウザでhttp:// localhost:9000 / errors/exceptionを開きましょう。 コードでは、アプリケーションがRuntimeExceptionをスローして、アプリケーションコードの障害をシミュレートしていることがわかります。 Play Frameworkを開発モードで実行すると、アプリケーションコードを含むエラーページが返され、問題の原因となった行が強調表示されます。
もちろん、アプリケーションコードをユーザーに表示することは、許容できないセキュリティリスクであるため、本番モードでは、エラーページに含まれる情報は少なくなります。
エラーメッセージで約束されているように、アプリケーションログを見ると、エラーIDとスタックトレースを含む行が表示されます。
! @7ibgcn351 - Internal server error, for (GET) [/errors/exception] ->
play.api.UnexpectedException: Unexpected exception[RuntimeException: Pretend that we have an application error.]
at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:355)
...
Caused by: java.lang.RuntimeException: Pretend that we have an application error.
at controllers.ErrorDemoController.$anonfun$exception$1(ErrorDemoController.scala:13)
4.2. InternalServerError 、 NotFound 、およびBadRequestを明示的に返す
ブラウザでhttp:// localhost:9000 / errors / internalerror URLを開くと、
http:// localhost:9000 / errors / notfoundページとhttp:// localhost:9000 / errors/badrequestページを開いたときにも同じことが起こります。 コントローラコードから明示的に返されたエラーステータスは、エラーハンドラによってインターセプトされません。
4.3. 実際のNotFoundページを取得する
もちろん、存在しないページにアクセスすると(たとえば、http:// localhost:9000 / this_is_not_here)、デフォルトの NotFound エラーページが表示されます(ここでも、開発モードのみ):
このシナリオを処理し、要求されたページが存在しない場合に別のページにリダイレクトするために、カスタムエラーハンドラーを定義できます。
5. カスタムエラーハンドラ
カスタムエラーハンドラの作成は、新しいエラーハンドラクラスの実装から始まります。
class CustomErrorHandler extends HttpErrorHandler {
def onClientError(request: RequestHeader, statusCode: Int, message: String): Future[Result] = {
if (statusCode == NOT_FOUND) {
// works only when we access a not existing page, not when we return NotFound on purpose
Future.successful(Redirect(routes.ErrorDemoController.noError()))
} else {
Future.successful(
Status(statusCode)("A client error occurred.")
)
}
}
def onServerError(request: RequestHeader, exception: Throwable): Future[Result] = {
Future.successful(
InternalServerError("A server error occurred: " + exception.getMessage)
)
}
}
ページが存在しない場合は、ユーザーを / errors /noerrorページにリダイレクトすることに注意してください。 したがって、ErrorDemoControllerに新しいメソッドを追加する必要があります。
def noError(): Action[AnyContent] = Action {
Ok
}
そして、routersファイルに新しいエントリを追加します。
GET /errors/noerror controllers.ErrorDemoController.noerror()
カスタムハンドラーを使用するには、application.confファイルで上書きする必要があります:
play.http.errorHandler = "errors.CustomErrorHandler"
6. カスタムエラーハンドラのテスト
それでは、ブラウザでhttp:// localhost:9000 / errors/exceptionページを開きましょう。 エラーメッセージが表示された赤いページと強調表示されたアプリケーションコードが表示される代わりに、「サーバーエラーが発生しました」というメッセージが表示された白いページが表示されます。 これは、 onServerErrorメソッドがエラーを処理し、応答を生成したことを意味します。
デフォルトのエラーハンドラーと同様に、カスタムハンドラーは、アプリケーションコードから明示的にサーバーエラーを返す場合、サーバーエラーをインターセプトしません。 http:// localhost:9000 / errors / internalerrorページを開くと、ブラウザはステータス500を受け取り、エラーページを表示します。
クライアント側のエラーをテストするときにも同じ動作を観察できます。 http:// localhost:9000 / errors / notfoundを開くと、404 HTTPステータスの応答が生成され、ブラウザに「ページが見つかりません」というメッセージが表示されます。 ただし、http:// localhost:9000 / this_is_not_hereページを開くと、 onClientErrorメソッドのカスタムコードが実行され、がhttp:// localhost:9000 / errors/noerrorにリダイレクトされます。ページ。
6.1. 自動テストの作成
ルートコントローラーの単体テストを作成する場合、フレームワークはテスト中にカスタムエラーハンドラーを使用しません。 このように動作して、例外をインターセプトするエラーハンドラーではなく、コントローラーコードをテストできるようにします。
エラーハンドラ自体をテストするには、を使用する個別の単体テストを実装する必要があります onClientError また onServerError 直接メソッド:
class CustomErrorHandlerSpec extends PlaySpec with GuiceOneAppPerTest with Injecting with Eventually {
"CustomErrorHandler" should {
"redirect when a page has not been found" in {
//given
val objectUnderTest = new CustomErrorHandler()
val request = FakeRequest(GET, "/fake")
val statusCode = StatusCodes.NotFound
val message = ""
//when
val responseFuture = objectUnderTest.onClientError(request, statusCode.intValue, message)
//then
eventually {
status(responseFuture) mustBe StatusCodes.SeeOther.intValue
}
}
}
}
7. 結論
この記事では、Play Frameworkでのデフォルトのエラー処理をテストし、カスタムエラーハンドラーを使用してその動作を変更し、エラーステータスを明示的に返してもエラーハンドラーがトリガーされないことを示しました。
いつものように、ソースコードはGitHubのにあります。