はじめに
Play frameworkのバージョン2.6の時点で、I18nを含むメッセージの処理を行うための実装に対してリファクタリングが行われたようで、設定ファイル(詳細については後述します。)に設定した文字列をプロパティ名を指定して取り出す方法が変更されました。この変更により、play.api.i18n.Messagesからメッセージを直接得ることができなくなってしまったため、カスタマイズしたエラーページにおいてメッセージの表示処理をするためには別の方法を検討せねばならなくなってしまいました。
そこで、この記事ではPlay frameworkのバージョン2.7.3(この記事を最初に書いた時点(2019年9月)での最新版です。)を利用しつつScalaで記述したWebアプリケーションにおいて、設定ファイルに設定した文字列をプロパティ名を指定して取り出し、カスタマイズしたエラーページ上に表示させるための設定および実装の手順について書きます。
カスタマイズしたエラーページに特別な実装等が必要な理由と対応策の概要
ScalaでPlay frameworkのコードを記述する場合、エラーページをカスタマイズする際にはHttpErrorHandlerを拡張したルートパッケージのクラスを実装するのが最も簡単です。しかし、HttpErrorHandlerはメッセージを処理するためのAPIを持っていないため、メッセージを処理するためのクラスをinjectする必要があります。
具体的には以下の実装を追加します。
- HttpErrorHandlerを拡張して作成したルートパッケージのクラス(以下、単に「エラーハンドラ」と書きます。)に対して、MessagesProviderトレイトをミックスインしたインスタンスの作成に必要なクラスをinjectするための実装。
- MessagesProviderトレイトをミックスインしたインスタンスをエラーハンドラのインスタンスで作成し、それをviewに渡すための実装。
- view側でメッセージを取り出すための実装。
設定および実装の手順
カスタマイズしたエラーページ上に表示させるには、以下の手順で設定および実装を行います。なお、本節で示すパスはbuild.sbtで指定されるディレクトリからの相対パスとします。例えば、build.sbtにおいて以下のように記述されている場合には、build.sbtのあるディレクトリがWebアプリケーションのプロジェクトのルートディレクトリとなります。
- conf/application.confに以下の設定を追加します。以下のように設定した場合、conf/messages.ja-JP という名前のファイルが必要で、そのファイルにはメッセージのプロパティ名に対応する文字列を日本語で記述しておく必要がありますが、記述がない場合にはconf/messagesファイル(ここまで「設定ファイル」と記述してきたものの実体です。以下、適宜「messageファイル」と書きます。)が参照されることと、この記事ではconf/messagesファイルの設定をカスタマイズしたエラーページ上に表示させることを優先したことから、conf/messages.ja-JPの作成及び設定の記述は行っておりません。
play.i18n.langs = [ “ja-JP” ]
- conf/messagesにカスタマイズしたエラーページ上に表示させるために必要な設定を記述します。Copyright表示のための設定です。
pandanote.copyright=©Copyright 2019, pandanote.info.
- エラーハンドラにLangsクラス及びMessagesApiクラスをinjectしてMessagesImplクラスのインスタンスを作成し、それをテンプレートに対してMessagesProviderトレイトとして渡すための実装を追加します。追加例は以下の通りです。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters// Sample Implementation to display a message from the message file. // See https://pandanote.info/?p=5370 for details. import org.slf4j.LoggerFactory import javax.inject.Inject import play.api.http.HttpErrorHandler import play.api.mvc.Results._ import play.api.mvc.RequestHeader import play.api.i18n.{ Langs, Lang, MessagesApi, MessagesImpl } import scala.concurrent.Future class SampleWebErrorHandler @Inject()(langs: Langs, messagesApi: MessagesApi) extends HttpErrorHandler { private lazy val logger = LoggerFactory.getLogger(classOf[SampleWebErrorHandler]) val lang = langs.availables.head override def onClientError(request: RequestHeader, statusCode: Int, message: String) = { logger.warn("ClientError: statusCode="+statusCode,message) Future.successful( Status(statusCode)(views.html.error.render(Option(statusCode), new MessagesImpl(lang, messagesApi))) ) } override def onServerError(request: RequestHeader, exception: Throwable) = { logger.error("ServerError:"+exception.toString,exception) Future.successful( InternalServerError(views.html.error.render(None, new MessagesImpl(lang, messagesApi))) ) } } - エラーページ用のテンプレートを修正します。具体的には以下の部分を変更します。
- MessagesProviderトレイトをimplicitなテンプレート引数に追加します。修正例は以下の通りです。
- テンプレート上でmessageファイルから文字列を取り出して表示させたい部分に以下のように記述します。implicitなMessagesオブジェクトを使用します。
- MessagesProviderトレイトをimplicitなテンプレート引数に追加します。修正例は以下の通りです。
- エラーページのテンプレートをエラーハンドラ以外のコードから呼び出している場合には、呼び出し元の引数に必要に応じてMessagesProviderトレイトが提供できるインスタンスを追加します。
- エラーページのテンプレートを呼び出すテンプレートについても、MessagesProviderトレイトが提供できるインスタンスがテンプレート引数に含まれていない場合には、必要に応じて3-aと同様の変更を行います。
動作の確認
ここまでの実装が完了したところで、動作確認を行います。
ブラウザを起動して、存在しないURLへのアクセスを試みます。
すると…
エラーページの下部にmessageファイルで設定した文字列が表示されていることが確認できます。
まとめ
Play frameworkの2.6以降を対象とした資料はこの記事を最初に書いた時点(2019年9月)ではあまり多くないように感じました。
「とりあえず動作をさせること」が目標の概要レベルの記述ではありますが、参考にしていただけると幸いです。
この記事は以上です。