scala play framework 專案如果要由 2.5 升級到 2.6,必須調整一些設定項目,另外 action composition 部分的程式也有更新的寫法,Lightbend activator 在 2017/5/24 已經退役,現在都要直接使用 sbt 編譯及封裝專案。
build.sbt
scala play 2.6 相關的 library 版本都要更新,scala 也要由 2.11 版改為 2.12,以下是 build.sbt
name := """project"""
organization := "tw.com.maxkit"
version := "0.1.0"
lazy val root = (project in file(".")).enablePlugins(PlayScala, JavaServerAppPackaging)
scalaVersion := "2.12.2"
scalacOptions ++= Seq("-encoding", "UTF-8")
libraryDependencies += guice
// Adds additional packages into Twirl
//TwirlKeys.templateImports += "tw.com.maxkit.controllers._"
// Adds additional packages into conf/routes
// play.sbt.routes.RoutesKeys.routesImport += "tw.com.maxkit.binders._"
libraryDependencies ++= Seq(
ws,
filters,
"com.typesafe.play" %% "play-slick" % "3.0.0",
"com.typesafe.play" %% "play-slick-evolutions" % "3.0.0",
// slick
"com.typesafe.slick" %% "slick" % "3.2.1",
"org.slf4j" % "slf4j-nop" % "1.6.4",
"com.typesafe.slick" %% "slick-hikaricp" % "3.2.1",
// 讓 slick 支援 Timestamp 轉換 slick-joda-mapper https://github.com/tototoshi/slick-joda-mapper
"com.github.tototoshi" %% "slick-joda-mapper" % "2.3.0",
"joda-time" % "joda-time" % "2.7",
"org.joda" % "joda-convert" % "1.7",
// akka remoteing
"com.typesafe.akka" % "akka-remote_2.12" % "2.5.3",
// smtp email plugin
// https://github.com/playframework/play-mailer
"com.typesafe.play" %% "play-mailer" % "6.0.0",
// mariadb java client
//"mysql" % "mysql-connector-java" % "5.1.36",
"org.mariadb.jdbc" % "mariadb-java-client" % "2.0.3",
// 使用 FileUtils
"commons-io" % "commons-io" % "2.5",
// 使用 Base64
"commons-codec" % "commons-codec" % "1.10",
// object pool
"commons-pool" % "commons-pool" % "1.6",
// CLI parser library scopt https://github.com/scopt/scopt
"com.github.scopt" % "scopt_2.12" % "3.6.0",
// redis for Play https://github.com/KarelCemus/play-redis
play.sbt.PlayImport.cacheApi,
// include play-redis library
"com.github.karelcemus" %% "play-redis" % "1.5.1",
// Test Framework
"org.scalatestplus.play" %% "scalatestplus-play" % "3.1.0" % Test,
specs2 % Test
)
resolvers ++= Seq(
"Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/",
"Typesafe Maven Repository" at "http://repo.typesafe.com/typesafe/maven-releases/",
"Typesafe ivy" at "http://dl.bintray.com/typesafe/ivy-releases",
"scalaz-bintray" at "https://dl.bintray.com/scalaz/releases"
)
fork in run := true
// production settings
maintainer := "service <service@maxkit.com.tw>"
packageSummary := "project"
packageDescription := """"""
project/plugins.sbt
resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.2")
project/build.properties
Action Composition
Actions Composition 2.6 官方文件
如果要在 controller 裡面每一個 method 都進行的登入的驗證,可以用 ActionComposition 的方式實作,但 2.6 版的 ActionComposition 已經調整為以下的做法。
首先獨立實作一個 AuthAction.scala
package controllers.admin
import java.sql.Timestamp
import java.util.Date
import javax.inject.Inject
import model.Usr
import play.api.{Environment, Logger, Mode}
import play.api.cache.redis.CacheApi
import play.api.mvc._
import utils.ServerConstants
import scala.concurrent.{ExecutionContext, Future}
class AuthRequest[A](val usr: Option[Usr], request: Request[A]) extends WrappedRequest[A](request)
class AuthAction @Inject()(cache: CacheApi,
env: Environment,
val parser: BodyParsers.Default)
(implicit val executionContext: ExecutionContext)
extends ActionBuilder[AuthRequest, AnyContent] with ActionTransformer[Request, AuthRequest] {
val cacheTimeout = ServerConstants.cacheTimeout
def transform[A](request: Request[A]) = Future.successful {
// val now: Timestamp = new Timestamp(new Date().getTime)
// val usr: Usr = new Usr(0, "", "", "", now, "", now, "")
//
// new AuthRequest(Some(usr), request)
(request.session.get("key").flatMap { key =>
cache.get[Usr](key)
} map { usr =>
// 需要再設定一次 cache,否則會發生 cache timeout
cache.set(request.session.get("key").get, usr, cacheTimeout)
new AuthRequest(Some(usr), request)
}).orElse {
env.mode match {
case Mode.Dev | Mode.Test => {
Logger.info("Mode.Dev don't check admin login status")
val now: Timestamp = new Timestamp(new Date().getTime)
val usr: Usr = new Usr(0, "", "devusr", "", now, "", now, "")
Some( new AuthRequest(Some(usr), request) )
}
case Mode.Prod => {
Some( new AuthRequest(None, request) )
}
}
}.get
}
}
在 controller 中,要用 injection 的方式將 AuthAction 引用進來。
@Singleton
class MyController @Inject()(
authAction: AuthAction,
actorSystem: ActorSystem, env: Environment,
implicit val executionContext: ExecutionContext,
cc: ControllerComponents) extends AbstractController(cc) {
def listCdrs = authAction.async { request: Request[AnyContent] =>
val body: AnyContent = request.body
val formdata: Option[Map[String, Seq[String]]] = body.asFormUrlEncoded
........
}
}
移除 Play.current
在 scala play 2.5 就已經不能用 Play.current,這裡記錄怎麼利用 injector 直接產生 instance。
在一般的 scala class 直接使用 database 的 model,已經不能用以下這種寫法,Play.current、DatabaseConfigProvider.get 都已經是 deprecated method。
val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current)
首先建立一個新的 GlobalContext
package modules
import play.api.inject.Injector
import javax.inject.{Inject,Singleton}
@Singleton
class GlobalContext @Inject()(playInjector: Injector) {
GlobalContext.injectorRef = playInjector
}
object GlobalContext {
private var injectorRef: Injector = _
def injector: Injector = injectorRef
}
在自訂的 Guice Module 中,產生 GlobalContext
bind(classOf[GlobalContext]).asEagerSingleton()
然後就能直接使用 injector 產生 database Model
val npRepo = GlobalContext.injector.instanceOf[NpRepo]
ref: How to access Play Framework 2.4 guice Injector in application?
Lightbend activator 在 2017/5/24 終止
因為 LIGHTBEND ACTIVATOR TEMPLATES 發布,未來已經不會再用 activator 進行 project template 的管理,要求大家改用 giter8 templates。
以往用 activator 產生新的 project 的指令,都要用 sbt 取代。
根據 giter8 template 產生新的 project
sbt new playframework/play-scala-seed.g8
在過程中,要填寫 project name, organization 等資料
This template generates a Play Scala project
name [play-scala-seed]: projectname
organization [com.example]: tw.com.maxkit
scalatestplusplay_version [3.1.1]:
play_version [2.6.2]:
在 poject 中,以往使用 activator 的指令,都要改成 sbt
編譯
啟動 server
封裝整個 project
sbt clean update compile stage dist
giter8
Giter8 是一個基於發佈在 Github 或任何 git 上的template來生成文件或目錄的命令行工具,它是以 Scala 實作並由 sbt launcher 運行。
除了一個官方的 giter8 project templates 集散地 之外,我們可以自己建立自己的 project template 並以 git 形式存放及分享在 git server 中。
References
Giter8 gitbook
Giter8
使用Scalatra創建Scala WEB工程
action-composition 2.5