Play2.4で非推奨、かつPlay2.5ではなくなっているGlobalSettingの消し方を紹介します。
なかなか大変なので、ブログにまとめておきました。
Global.scalaで書いていたコード
Play2.3で書いていたGlobal.scalaのコードを記載します。
object Global extends WithFilters(CsrfFilter, AccessLoggingFilter, SystemAccessFilter) with GlobalSettings with JsonHelper { override def onStart(app: Application) { Akka.system.scheduler.schedule(1 hour, 1 hour) { Image.cleanTempImage() } } override def onError(request: RequestHeader, ex: Throwable): Future[Result] = { Logger.warn(s"Internal server error. Messages : ${e.getMessage} ExceptionMessage: ${e.getMessage()}") } } /** * アクセスログ出力フィルター */ object AccessLoggingFilter extends Filter { import models.component.RequestComponent._ val accessLogger = Logger("access") def apply(next: (RequestHeader) => Future[Result])(request: RequestHeader): Future[Result] = { val resultFuture = next(request) val startTime = System.currentTimeMillis resultFuture.flatMap { result => val takeTime = System.currentTimeMillis - startTime val msg = s"method=${request.method} uri=${request.uri} remote-address=${request.ipAddr} status=${result.header.status} user-agent=${request.userAgent} takeTime=${takeTime} ms" if (takeTime >= 1000) accessLogger.warn(s"*** ${msg} ***") else accessLogger.info(msg) resultFuture } recover { case NonFatal(ex) => { val takeTime = System.currentTimeMillis - startTime val msg = s"method=${request.method} uri=${request.uri} remote-address=${request.ipAddr} status=500 user-agent=${request.userAgent} takeTime=${takeTime}" accessLogger.error(msg) throw ex } } } }
onErrorを無くす
まず簡単なところから。
GlobalSettingを継承しているclassでonErrorが定義されていると思いますが、
そこをErrorHandlerに移します。
以下は2.3で書いていたことです。
def onError(request: RequestHeader, ex: Throwable): Future[Result] = { Logger.warn(s"Internal server error. Messages : ${e.getMessage} ExceptionMessage: ${e.getMessage()}") }
新しくErrorHandlerというクラスを作ります。
アプリケーションのロジック内で自前で発生させた例外はonServerErrorに入るので、onServerErrorのコードが多くなると思います。
個人的には、controller系と同じパッケージに作るほうがいいと思います。
import play.api.http.HttpErrorHandler import play.api.mvc._ import play.api.mvc.Results._ import scala.concurrent._ class ErrorHandler extends HttpErrorHandler { def onClientError(request: RequestHeader, statusCode: Int, message: String) = { Future.successful( Status(statusCode)("A client error occurred: " + message) ) } def onServerError(request: RequestHeader, exception: Throwable) = { Future.successful( InternalServerError("A server error occurred: " + exception.getMessage) ) } }
また、作ったErrorHandlerが呼び出されるようにapplication.confに以下を追記しましょう。
play.http.errorHandler="controllers.ErrorHandler"
各パラメータに対して、細かく処理を設定するのであれば、以下リンクにあるようにDefaultErrorHandlerを継承して作ったほうがいいかもしれません。
ScalaErrorHandling - 2.4.x
Filter系
以下のようなFilterを統合するclassを作るだけでOKです。(objectの場合はclassにしてあげましょう)
AccessFilterの実装などは、2.3で書いていたものと同じでOKです。
Global.scalaなどでwithFilterを継承していた場合は、それを取り除きましょう。
package filters import javax.inject.Inject import play.api.http.HttpFilters class Filters @Inject() ( csrf: CsrfFilter, access: AccessLoggingFilter, system: SystemAccessFilter) extends HttpFilters { val filters = Seq(csrf, access, system) }
onErrorと同じようにapplication.confに以下を追記します。
play.http.filters="filters.Filters"
onStart
onStartはちょっと面倒です。
まず以下のようなclassを作ります。
これは、実際に起動時に実行する処理を記載します。
その際、Singletonアノテーションをつけます。
Singletonアノテーションをつけることでobjectのように一度しか生成されなくなります。
じゃあscalaなんだからobjectでいいじゃねーか。ってなるかもしれませんが、objectだとInjectができないので、Play内部では使いづらくなってしまうので、classで定義しましょう。
package modules import javax.inject.{ Inject, Singleton } import akka.actor.ActorSystem import com.google.inject.ImplementedBy import models.Image import play.api.inject.ApplicationLifecycle import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @ImplementedBy(classOf[IntervalJobs]) trait Interval @Singleton class IntervalJobs @Inject() ( system: ActorSystem, lifeCycle: ApplicationLifecycle) extends Interval { import scala.concurrent.duration._ val hourJob = system.scheduler.schedule(1.hour, 1.hour)(Image.cleanTempImage()) lifeCycle.addStopHook { () => Future.successful(hourJob.cancel()) } }
更にこの起動時に実行するclassをmoduleというclassでまとめます。
package modules import com.google.inject.AbstractModule import com.google.inject.name.Names class InitialModule extends AbstractModule { override def configure(): Unit = { bind(classOf[Interval]) .annotatedWith(Names.named("interval")) // ここは名前をつけたくなければいらない。 .to(classOf[IntervalJobs]).asEagerSingleton() } }
そして、お決まりのapplication.confに以下を記載
play.modules.enabled += "modules.InitialModule"
Message系
Meesagesをつかっている場合、今まではimplicitをimportしておけば動いていましたが、2.4からはMessageApiを使わないとコンパイルエラーになります。
以下のようにMessagesApiをInjectし、I18nSupportをMixinしてあげれば、動きます。
import javax.inject.Inject import scala.concurrent.ExecutionContext.Implicits.global import consts.AppConfig import play.api.i18n.{ I18nSupport, Messages, MessagesApi } import play.api.mvc.{ Filter, RequestHeader, Result } import play.api.mvc.Results._ import scala.concurrent.Future class SystemAccessFilter @Inject() (val messagesApi: MessagesApi) extends Filter with I18nSupport { def apply(next: (RequestHeader) => Future[Result])(request: RequestHeader): Future[Result] = { if (!ipCheck(request)) { Future { NotFound(Messages("error.notFound")) } } else { next(request) } } }
まとめ
結構構造的に大幅に変わっていますが、Injectのイメージが付けば、理解は早いかなーと思います。
大体Playのドキュメントに沿った形で解説しました。
これを読んで本家のドキュメントの理解が進めば幸いです。