文件

§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 或小於 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 檔案的設定區塊中

requireJs := "main.js"

可以在 RequireJS 文件頁面 上找到更多關於此功能的資訊。

下一頁:Scala 3 遷移指南


在這個文件中發現錯誤了嗎?此頁面的原始碼可以在 這裡 找到。在閱讀 文件指南 後,請隨時提出 pull request。有問題或建議要分享嗎?請前往 我們的社群論壇 與社群展開對話。