文档

§Play 2.1 迁移指南

本指南介绍如何从 Play 2.0 迁移到 Play 2.1。

要将 **Play 2.0.x** 应用程序迁移到 **Play 2.1.0**,首先在 project/plugins.sbt 文件中更新 Play 的 sbt-plugin

addSbtPlugin("play" % "sbt-plugin" % "2.1.0")

现在更新 project/Build.scala 文件,使用新的 play.Project 类代替 PlayProject

首先导入

import play.Project._

然后创建 main 项目

val main = play.Project(appName, appVersion, appDependencies).settings(

最后,更新您的 project/build.properties 文件

sbt.version=0.12.2

然后使用 **Play 2.1.0** 发行版中的 play 命令清理并重新编译您的项目

play clean
play ~run

如果出现任何编译错误,本文档将帮助您找出可能导致错误的弃用或不兼容更改。

§构建文件更改

由于 Play 2.1 引入了进一步的模块化,您现在必须明确指定应用程序所需的依赖项。默认情况下,任何 play.Project 仅包含对核心 Play 库的依赖项。您必须选择应用程序所需的精确可选依赖项集。以下是 **Play 2.1** 中新的模块化依赖项

以下是 **Play 2.1** 的典型 Build.scala 文件

import sbt._
import Keys._
import play.Project._

object ApplicationBuild extends Build {

    val appName         = "app-name"
    val appVersion      = "1.0"

    val appDependencies = Seq(
       javaCore, javaJdbc, javaEbean
    )

    val main = play.Project(appName, appVersion, appDependencies).settings(
      // Add your own project settings here      
    )

}

项目的 mainLang 参数不再需要。主要语言是根据添加到项目中的依赖项确定的。如果依赖项包含 javaCore,则语言设置为 JAVA,否则设置为 SCALA。请注意 appDependencies 部分中的模块化依赖项。

§play.mvc.Controller.form() 重命名为 play.data.Form.form()

同样与模块化相关,play.data 包及其依赖项已从 Play 核心移至 javaCore 工件。因此,play.mvc.Controller#form 已移至 play.data.Form#form

§play.db.ebean.Model.Finder.join() 重命名为 fetch()

作为清理工作的一部分,Finder API 的 join 方法被 fetch 方法取代。它们的行为完全相同。

§Play 的 Promise 成为 Scala 的 Future

随着 Scala 2.10 中引入 scala.concurrent.Future,Scala 生态系统在统一各种 Future 和 Promise 库方面取得了巨大进步。

Play 现在直接使用 scala.concurrent.Future,这意味着用户可以轻松地组合来自内部 API 或外部库的 future/promise。

Java 用户目前将继续使用 Play 对 scala.concurrent.Future 的包装器。

考虑以下代码片段

import play.api.libs.iteratee._
import play.api.libs.concurrent._
import akka.util.duration._

def stream = Action {
    AsyncResult {
      implicit val timeout = Timeout(5.seconds)
      val akkaFuture =  (ChatRoomActor.ref ? (Join()) ).mapTo[Enumerator[String]]
      //convert to play promise before sending the response
      akkaFuture.asPromise.map { chunks =>
        Ok.stream(chunks &> Comet( callback = "parent.message"))
      }
    }
  }
  

使用新的 scala.concurrent.Future,这将变为

import play.api.libs.iteratee._
import play.api.libs.concurrent._
import play.api.libs.concurrent.Execution.Implicits._

import scala.concurrent.duration._

  def stream = Action {
    AsyncResult {
      implicit val timeout = Timeout(5.seconds)
      val scalaFuture = (ChatRoomActor.ref ? (Join()) ).mapTo[Enumerator[String]]
      scalaFuture.map { chunks =>
        Ok.stream(chunks &> Comet( callback = "parent.message"))
      }
    }
  }

注意为以下内容添加的额外导入:

一般来说,如果您看到错误消息“error: could not find implicit value for parameter executor”,您可能需要添加

import play.api.libs.concurrent.Execution.Implicits._

(有关更多信息,请参阅 Scala 关于执行上下文的文档)

请记住

§Scala JSON API 的更改

Play 2.1 带来了一个全新的 Scala JSON 验证器和路径导航器。但是,这个新的 API 与现有的 JSON 解析器不兼容。

play.api.libs.json.Reads 类型签名已更改。考虑

trait play.api.libs.json.Reads[A] {
  self =>

  def reads(jsValue: JsValue): A

}

在 2.1 中,这将变为

trait play.api.libs.json.Reads[A] {
  self =>

  def reads(jsValue: JsValue): JsResult[A]

}

因此,在 Play 2.0 中,User 类型的 JSON 序列化器的实现为

implicit object UserFormat extends Format[User] {

  def writes(o: User): JsValue = JsObject(
    List("id" -> JsNumber(o.id),
      "name" -> JsString(o.name),
      "favThings" -> JsArray(o.favThings.map(JsString(_)))
    )
  )

  def reads(json: JsValue): User = User(
    (json \ "id").as[Long],
    (json \ "name").as[String],
    (json \ "favThings").as[List[String]]
  )

}

在 **Play 2.1** 中,你需要将其重构为

implicit object UserFormat extends Format[User] {

  def writes(o: User): JsValue = JsObject(
    List("id" -> JsNumber(o.id),
      "name" -> JsString(o.name),
      "favThings" -> JsArray(o.favThings.map(JsString(_)))
    )   
  )   

  def reads(json: JsValue): JsResult[User] = JsSuccess(User(
    (json \ "id").as[Long],
    (json \ "name").as[String],
    (json \ "favThings").as[List[String]]
  ))  

}

生成 JSON 的 API 也发生了变化。请考虑

val jsonObject = Json.toJson(
  Map(
    "users" -> Seq(
      toJson(
        Map(
          "name" -> toJson("Bob"),
          "age" -> toJson(31),
          "email" -> toJson("[email protected]")
        )
      ),
      toJson(
        Map(
          "name" -> toJson("Kiki"),
          "age" -> toJson(25),
          "email" -> JsNull
        )
      )
    )
  )
)

在 **Play 2.1** 中,这变成了

val jsonObject = Json.obj(
  "users" -> Json.arr(
    Json.obj(
      "name" -> "Bob",
      "age" -> 31,
      "email" -> "[email protected]"
    ),
    Json.obj(
      "name" -> "Kiki",
      "age" -> 25,
      "email" -> JsNull
    )
  )
)

有关这些功能的更多信息,请参见 JSON 文档

由于 JBoss Netty 的更改,Cookie 通过将它们的 maxAge 设置为 nullNone(取决于 API)来使其成为瞬态的,而不是将 maxAge 设置为 -1。任何等于或小于 0 的 maxAge 值都将导致 Cookie 立即过期。

SimpleResult 上的 discardingCookies(String\*)(Scala)和 discardCookies(String...)(Java)方法已弃用,因为这些方法无法处理在特定路径、域或设置为安全的 Cookie。请改用 discardingCookies(DiscardingCookie*)(Scala)和 discardCookie(Java)方法。

§RequireJS

在 **Play 2.0** 中,Javascript 的默认行为是使用 Google 的 Closure CommonJS 模块支持。在 **Play 2.1** 中,这已更改为使用 RequireJS。

实际上这意味着,默认情况下,Play 仅在 stage、dist、start 模式下才会最小化和合并文件。在 dev 模式下,Play 将在客户端解析依赖项。

如果你想使用此功能,你需要将你的模块添加到 project/Build.scala 文件的 settings 块中

requireJs := "main.js"

有关此功能的更多信息,请参见 RequireJS 文档页面

下一步:Scala 3 迁移指南


在此文档中发现错误?此页面的源代码可以在 这里 找到。在阅读 文档指南 后,请随时贡献拉取请求。有疑问或建议要分享?前往 我们的社区论坛 与社区开始对话。