文件

§Play 2.3 遷移指南

這是從 Play 2.2 遷移到 Play 2.3 的指南。如果您需要從 Play 的早期版本遷移,則必須先遵循 Play 2.2 遷移指南

§Activator

在 Play 2.3 中,play 指令已變更為 activator 指令。Play 已更新為使用 Activator

§Activator 指令

play 指令中提供的所有功能在 activator 指令中仍然可用。

新的 activator 指令和舊的 play 指令都是 sbt 的包裝器。如果您願意,可以直接使用 sbt 指令。但是,如果您使用 sbt,您將會錯過 Activator 的多項功能,例如範本 (activator new) 和 Web 使用者介面 (activator ui)。sbt 和 Activator 都支援所有常用的主控台指令,例如 testrun

§Activator 發行版

Play 以 Activator 發行版形式發行,其中包含 Play 的所有依賴項。您可以從 Play 下載 頁面下載此發行版。

如果您願意,您也可以從 Activator 網站 下載 Activator 的精簡版 (1MB)。在下載頁面尋找「精簡版」發行版。精簡版的 Activator 僅會在需要時下載依賴項。

由於 Activator 是 sbt 的包裝器,如果您願意,也可以直接下載並使用 sbt

§建置變更

§sbt

Play 使用 sbt 0.13.5。如果您要更新現有專案,請變更您的 project/build.properties 檔案為

sbt.version=0.13.5

§外掛程式變更

變更 project/plugins.sbt 中的 Play 外掛程式版本

addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.XXX")

其中 2.3.XXX 是您要使用的 Play 版本。

您還需要新增一些 sbt-web 外掛程式,請參閱下列的 sbt-web 區段。

§自動外掛程式和外掛程式設定

sbt 0.13.5 帶來一個名為「自動外掛程式」的新功能。

自動外掛允許 sbt 外掛在 project 資料夾中宣告(通常是 plugins.sbt),如同以往。但改變的是,外掛現在可以宣告它們對其他外掛的需求,以及在給定的建置中觸發它們啟用的條件。在自動外掛之前,新增到建置中的外掛總是可用的;現在外掛會針對給定的模組有選擇地啟用。

這對你來說的意思是,宣告 addSbtPlugin 可能不足以用於現在使用自動外掛功能的外掛。這是好事。現在你可以選擇專案的哪些模組應該具有哪些外掛,例如

lazy val root = (project in file(".")).enablePlugins(SbtWeb)

上述範例顯示 SbtWeb 已新增到建置的根專案。在 SbtWeb 的情況下,如果它啟用,還有其他外掛也會啟用,例如,如果你也透過 addSbtPlugin 新增 sbt-less-plugin,它將會啟用,僅是因為 SbtWeb 已啟用。因此,SbtWeb 是該類別外掛的「根」外掛。

Play 本身現在使用自動外掛機制新增。已移除 Play 2.2 中使用 playJavaSettingsplayScalaSettings 的機制。現在你改用下列其中一項

lazy val root = (project in file(".")).enablePlugins(PlayJava)

lazy val root = (project in file(".")).enablePlugins(PlayScala)

如果你先前使用 play.Project,例如 Scala 專案

object ApplicationBuild extends Build {

  val appName = "myproject"
  val appVersion = "1.0-SNAPSHOT"

  val appDependencies = Seq()

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

}

…那麼你可以繼續透過原生 sbt 使用類似的方法

object ApplicationBuild extends Build {

  val appName = "myproject"
  val appVersion = "1.0-SNAPSHOT"

  val appDependencies = Seq()

  val main = Project(appName, file(".")).enablePlugins(play.PlayScala).settings(
    version := appVersion,
    libraryDependencies ++= appDependencies
  )

}

透過移至上述樣式,設定現在會在啟用外掛時自動匯入。

Play 所提供的金鑰現在也必須在 PlayKeys 物件內參考。例如,要參考 playVersion,您必須透過匯入

import PlayKeys._

或使用 PlayKeys.playVersion 進行限定。

在使用 .sbt 檔案以外,亦即如果您使用 Scala 來描述您的建置,則可以執行下列動作,讓 PlayKeys 在範圍內

import play.Play.autoImport._
import PlayKeys._

§明確的 scalaVersion

Play 2.3 支援 Scala 2.11 和 Scala 2.10。Play 外掛先前已為您設定 scalaVersion sbt 設定。現在您應該指出您想要使用的 Scala 版本。

更新您的 build.sbtBuild.scala 以納入 Scala 版本

對於 Scala 2.11

scalaVersion := "2.11.1"

對於 Scala 2.10

scalaVersion := "2.10.4"

§sbt-web

Play 2.3 最大的新功能是導入 sbt-web。總之,sbt-web 允許將 Html、CSS 和 JavaScript 功能從 Play 的核心抽取到純粹 sbt 外掛的系列中。對您而言,有兩個主要的優點

還有其他優點,包括 sbt-web 外掛程式能夠透過 Trireme 在 JVM 內執行,或使用 Node.js 原生執行。請注意,sbt-web 是一種開發環境,不會參與 Play 應用程式的執行。預設使用 Trireme,但如果您已安裝 Node.js,而且想要為您的建置提供極佳的效能,那麼您可以透過 sbt 的 SBT_OPTS 環境變數提供系統屬性。例如

export SBT_OPTS="$SBT_OPTS -Dsbt.jse.engineType=Node"

sbt-web 的一項功能是,它不會在意您使用「javascripts」或「stylesheets」作為您的資料夾名稱。任何具有適當檔名副檔名的檔案都會從 app/assets 資料夾中過濾出來。

sbt-web 的一個細微差別是,所有 資產都從 public 資料夾提供。因此,如果您之前有資產位於 public 資料夾之外,也就是說,您使用了 playAssetsDirectories 設定,如下例所示

playAssetsDirectories <+= baseDirectory / "foo"

…那麼您現在應該使用下列設定

unmanagedResourceDirectories in Assets += baseDirectory.value / "foo"

…不過請注意,那裡的檔案會聚合到目標 public 資料夾。這表示「public/a.js」中的檔案會被「foo/a.js」中的檔案覆寫。或者,使用專案 public 資料夾的子資料夾來為它們命名空間。

下列清單列出所有 sbt-web 相關元件及其在發佈 Play 2.3 時的版本。

§函式庫

"com.typesafe" %% "webdriver" % "1.0.0"
"com.typesafe" %% "jse" % "1.0.0"
"com.typesafe" %% "npm" % "1.0.0"

§sbt 外掛程式

"com.typesafe.sbt" % "sbt-web" % "1.0.0"
"com.typesafe.sbt" % "sbt-webdriver" % "1.0.0"
"com.typesafe.sbt" % "sbt-js-engine" % "1.0.0"

"com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0"
"com.typesafe.sbt" % "sbt-digest" % "1.0.0"
"com.typesafe.sbt" % "sbt-gzip" % "1.0.0"
"com.typesafe.sbt" % "sbt-less" % "1.0.0"
"com.typesafe.sbt" % "sbt-jshint" % "1.0.0"
"com.typesafe.sbt" % "sbt-mocha" % "1.0.0"
"com.typesafe.sbt" % "sbt-rjs" % "1.0.1"

§WebJars

WebJars 現在在提供資產給 Play 應用程式方面扮演重要的角色。例如,您可以宣告您將使用熱門的 Bootstrap 函式庫,只要在您的建置檔案中加入下列依賴關係即可

libraryDependencies += "org.webjars" % "bootstrap" % "3.2.0"

WebJars 會自動解壓縮到與您的公開資產相對應的 lib 資料夾中,以方便使用。例如,如果您宣告對 RequireJs 的依賴關係,那麼您可以使用類似下列的程式碼行從檢視中參照它

<script data-main="@routes.Assets.at("javascripts/main.js")" type="text/javascript" src="@routes.Assets.at("lib/requirejs/require.js")"></script>

請注意 lib/requirejs/require.js 路徑。lib 資料夾表示解壓縮的 WebJar 資產,requirejs 資料夾對應到 WebJar artifactId,而 require.js 參照 WebJar 根目錄中的所需資產。

§npm

npm 可以用,WebJars 也可以,只要在專案根目錄宣告一個 package.json 檔案。npm 套件的資源會萃取到與 WebJars 相同的 lib 資料夾,因此,從程式碼的角度來看,資源是來自 WebJar 還是 npm 套件,並無差別。


從您的角度來看,我們的目標是提供與 Play 先前版本功能同等的 sbt-web。雖然內部已大幅變動,但對您來說,轉換應該是微小的。本節的其餘部分將探討 Play 中每個已由 sbt-web 取代的部分,並說明應變更的內容。

§CoffeeScript

您現在必須宣告外掛程式,通常在您的 plugins.sbt 檔案中

addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0")

Coffeescript 選項已變更。新的選項為

CoffeeScriptKeys.sourceMap := true

CoffeeScriptKeys.bare := false

如需更多資訊,請參閱 外掛程式的文件

§LESS

您現在必須宣告外掛程式,通常在您的 plugins.sbt 檔案中

addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.0")

現在使用篩選器宣告進入點。例如,宣告需要 foo.lessbar.less

includeFilter in (Assets, LessKeys.less) := "foo.less" | "bar.less"

如果您先前使用過一種行為,其中檔名在 less 檔名前面加上底線的檔案會被忽略,但所有其他 less 檔案都會編譯,請使用下列篩選器

includeFilter in (Assets, LessKeys.less) := "*.less"

excludeFilter in (Assets, LessKeys.less) := "_*.less"

與 Play 2.2 不同,sbt-less 外掛程式允許任何使用者下載原始 LESS 原始碼檔案和產生的原始碼對應。它允許在現代網路瀏覽器中更輕鬆地進行偵錯。此功能即使在生產模式中也會啟用。

外掛程式的選項為

選項 說明
cleancss 使用 clean-css 壓縮輸出。
cleancssOptions 傳遞選項以清除 CSS,使用 https://github.com/GoalSmashers/clean-css 的 CLI 參數。
color LESS 輸出是否應加上色彩
compress 移除部分空白以壓縮輸出。
ieCompat 執行 IE 相容性檢查。
insecure 允許從不安全的 https 主機匯入。
maxLineLen 最大行長度。
optimization 設定剖析器的最佳化層級。
relativeUrls 將相對 URL 重寫到基礎 less 檔案。
rootpath 設定相對匯入和 URL 中 URL 重寫的根路徑。
silent 抑制錯誤訊息的輸出。
sourceMap 輸出 v3 原始碼地圖。
sourceMapFileInline 原始碼地圖是否應嵌入到輸出檔案中
sourceMapLessInline 是否將 less 程式碼嵌入到原始碼地圖中
sourceMapRootpath 將此路徑新增到原始碼地圖檔名和 less 檔案路徑中。
strictImports 匯入是否應嚴格。
strictMath 需要括號。此選項可能預設為 true,並在未來移除。
strictUnits 所有單位是否應嚴格,或允許混合單位。
verbose 詳細說明。

如需更多資訊,請參閱 外掛程式文件

§Closure Compiler

Closure Compiler 已被取代。其驗證 JavaScript 和壓縮 JavaScript 的兩個重要功能已分解成 JSHintUglifyJS 2

若要使用 JSHint,您必須宣告它,通常在您的 plugins.sbt 檔案中

addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.0")

選項可以根據 JSHint 網站 指定,且它們共用相同的預設值。若要設定選項,您可以在專案的基礎目錄中提供一個 .jshintrc 檔案。如果沒有此類檔案,系統會在您的家目錄中搜尋 .jshintrc 檔案。此行為可以使用外掛程式的 JshintKeys.config 設定來覆寫。
JshintKeys.config 用於指定組態檔的位置。

如需更多資訊,請參閱 外掛程式的文件

UglifyJS 2 目前透過 RequireJS 外掛程式提供(如下所述)。未來的目標是提供一個獨立的 UglifyJS 2 外掛程式,以適用於未使用的 RequireJS 的情況。

§RequireJS

RequireJS Optimizer (rjs) 已完全替換為一個應該更容易使用的程式。新的 rjs 是 sbt-web 的資產管線功能的一部分。與每次建置都會呼叫的前身不同,新的 rjs 只有在透過 Play 的 stagedist 任務產生發行版時才會呼叫。

若要使用 rjs,您必須宣告它,通常在您的 plugins.sbt 檔案中

addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.1")

若要將外掛程式新增至資產管線,您可以宣告如下

pipelineStages := Seq(rjs)

我們也建議將 sbt-web 的 sbt-digest 和 sbt-gzip 外掛程式包含在管線中。sbt-digest 會提供 Play 的資產控制器,以指紋辨識資產名稱,以進行遠端快取。sbt-gzip 會產生資產的 gzip,資產控制器在收到請求時會偏好使用此 gzip。您的 plugins.sbt 檔案的此組態會如下所示

addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.0.0")

addSbtPlugin("com.typesafe.sbt" % "sbt-gzip" % "1.0.0")

而您的管線組態現在會變成

pipelineStages := Seq(rjs, digest, gzip)

階段的順序很重要。您首先要最佳化檔案,產生它們的摘要,然後產生所有結果資產的 gzip 版本。

RequireJs 最佳化的選項已完全變更。外掛程式的選項為

選項 說明
appBuildProfile 專案建置設定檔內容。
appDir 包含 app js 檔案的最上層目錄。實際上,這是 rjs 讀取的來源資料夾。
baseUrl js 檔案存放的資產或公開資料夾的相對目錄。預設為「js」、「javascripts」或「.」;若找不到前兩個,則使用後者。
buildProfile 建置設定檔金鑰 -> 除了 appBuildProfile 提供的預設值之外的值設定。此處的任何設定也會取代任何預設值。
dir 預設情況下,所有模組都位於此路徑的相對位置。實際上,這是 rjs 的目標目錄。
generateSourceMaps 預設情況下,會產生原始碼對應表。
mainConfig 預設情況下,使用「main」作為設定檔的模組。
mainConfigFile 上述設定檔的完整路徑。
mainModule 預設情況下,使用「main」作為模組。
modules 模組的 json 陣列。
optimize 最佳化器的名稱,預設為 uglify2。
paths RequireJS 路徑對應,將模組 ID 對應到建置路徑和生產路徑的組。預設情況下,所有 WebJar 函式庫都可從 CDN 取得,且其對應可在此處找到(除非將 cdn 設定為 None)。
preserveLicenseComments 是否保留註解。由於有原始碼對應表,因此預設為 false(請參閱 https://requirejs.dev.org.tw/docs/errors.html#sourcemapcomments)
webJarCdns 用於尋找 WebJar 的 CDN。預設情況下,「org.webjars」對應到「jsdelivr」。
webJarModuleIds 要使用的 WebJar 模組 ID 順序。

如需更多資訊,請參閱 外掛程式文件

§預設 ivy 本地儲存庫和快取

由於 Play 現在使用 Activator 作為啟動器,因此現在使用預設的 ivy 快取和本機儲存庫。這表示之前發佈到您依賴的 Play ivy 快取的任何內容都需要發佈到家目錄中的 .ivy2 資料夾中的本機 ivy 儲存庫。

§結果重組

在 Play 2.2 中,許多結果類型已棄用,且為了促進移轉到新的結果結構,引入了某些新類型。Play 2.3 完成了此重組。

§Scala 結果

已移除 Play 2.1 中下列已棄用的類型和輔助程式

如果您有仍使用這些內容的程式碼,請參閱 Play 2.2 移轉指南 以瞭解如何移轉到新的結果結構。

正如 2.2 中所規劃,2.3 已將 play.api.mvc.SimpleResult 重新命名為 play.api.mvc.Result(取代現有的 Result 特質)。已引入類型別名以促進移轉,因此您的 Play 2.2 程式碼應與 Play 2.3 相容,但我們最終會移除此類型別名,因此我們已將其棄用,並建議切換到 Result

§Java 結果

已移除 Play 2.1 中下列已棄用的類型和輔助程式

如果您有仍使用這些內容的程式碼,請參閱 Play 2.2 移轉指南 以瞭解如何移轉到新的結果結構。

正如 2.2 中所規劃,2.3 已將 play.mvc.SimpleResult 重新命名為 play.mvc.Result。這對於大多數 Java 程式碼應是透明的。此變更影響最顯著的地方是在 Global.java 錯誤回呼和自訂動作中。

§範本

範本引擎現在是一個獨立專案,Twirl

§內容類型

範本內容類型已移至 twirl 套件。如果參照 play.mvc.Content 類型,則需要將其更新為 play.twirl.api.Content。例如,Play Java 專案中的下列程式碼

import play.mvc.Content;

Content html = views.html.index.render("42");

將產生錯誤

[error] ...: incompatible types
[error] found   : play.twirl.api.Html
[error] required: play.mvc.Content

並需要匯入 play.twirl.api.Content

§sbt 設定

範本的 sbt 設定現在由 sbt-twirl 外掛程式提供。

先前新增其他匯入至範本的方式為

templatesImport += "com.abc.backend._"

現在則為

TwirlKeys.templateImports += "org.abc.backend._"

先前自訂範本格式的方式為

templatesTypes += ("html" -> "my.HtmlFormat.instance")

現在則為

TwirlKeys.templateFormats += ("html" -> "my.HtmlFormat.instance")

對於使用完整 Scala 語法的 sbt 建置,可以使用下列方式匯入 TwirlKeys

import play.twirl.sbt.Import._

§Html.empty 已由 HtmlFormat.empty 取代

如果您先前使用 Html.empty (play.api.templates.Html.empty),現在必須使用 play.twirl.api.HtmlFormat.empty

§Play WS

WS 伺服器端現在是一個選用函式庫。如果您在專案中使用 WS,則需要新增函式庫相依性。對於 Java 專案,您還需要更新至新的套件。

§Java 專案

將函式庫相依性新增至 build.sbt

libraryDependencies += javaWs

在原始檔中更新至新的函式庫套件

import play.libs.ws.*;

§Scala 專案

將函式庫相依性新增至 build.sbt

libraryDependencies += ws

此外,使用 WS 伺服器端現在需要在範圍內有一個 Play 應用程式。通常這是透過新增下列方式來達成

import play.api.Play.current

WS API 已略微變更,而 WS.client 現在傳回 WSClient 的執行個體,而非基礎的 AsyncHttpClient 物件。您可以透過呼叫 WS.client.underlying 來取得 AsyncHttpClient

§Anorm

此新版本中包含了 Anorm 的各種變更。

為了改善類型安全性,查詢參數的類型必須可見,以便可以正確轉換。現在,使用 Any 作為參數值(無論是明確使用或因擦除而使用)會導致編譯錯誤 No implicit view available from Any => anorm.ParameterValue

// Wrong
val p: Any = "strAsAny"
SQL("SELECT * FROM test WHERE id={id}").
  on('id -> p) // Erroneous - No conversion Any => ParameterValue

// Right
val p = "strAsString"
SQL("SELECT * FROM test WHERE id={id}").on('id -> p)

// Wrong
val ps = Seq("a", "b", 3) // inferred as Seq[Any]
SQL("SELECT * FROM test WHERE (a={a} AND b={b}) OR c={c}").
  on('a -> ps(0), // ps(0) - No conversion Any => ParameterValue
    'b -> ps(1), 
    'c -> ps(2))

// Right
val ps = Seq[anorm.ParameterValue]("a", "b", 3) // Seq[ParameterValue]
SQL("SELECT * FROM test WHERE (a={a} AND b={b}) OR c={c}").
  on('a -> ps(0), 'b -> ps(1), 'c -> ps(2))

如果需要傳遞沒有安全轉換的值,可以使用 anorm.Object(anyVal) 來設定不透明參數。

此外,也修正了參數值有關擦除的問題:類型不再是 ParameterValue[_],而是單純的 ParameterValue

參數名稱的類型也已統一(使用 .on(...) 時)。現在僅支援 StringSymbol 作為名稱。

類型 Pk[A] 已棄用。您仍然可以使用它作為欄位對應,但需要明確傳遞 Id[A]NotAssigned 作為查詢參數(作為類型安全性改善的結果)

// Column mapping, deprecated but Ok
val pk: Pk[Long] = SQL("SELECT id FROM test WHERE name={n}").
  on('n -> "mine").as(get[Pk[Long]].single)

// Wrong parameter
val pkParam: Pk[Long] = Id(1l)
val name1 = "Name #1"
SQL"INSERT INTO test(id, name) VALUES($pkParam, $name1)".execute()
// ... pkParam is passed as Pk in query parameter, 
// which is now wrong as a parameter type (won't compile)

// Right parameter Id
val idParam: Id[Long] = Id(2l) // same as pkParam but keep explicit Id type
val name2 = "Name #2"
SQL"INSERT INTO test(id, name) VALUES($idParam, $name2)".execute()

// Right parameter NotAssigned
val name2 = "Name #3"
SQL"INSERT INTO test(id, name) VALUES($NotAssigned, $name2)".execute()

由於已棄用的 Pk[A] 類似於 Option[A](Anorm 在欄位對應和查詢參數中支援 Option[A]),因此建議將 Id[A] 替換為 Some[A],將 NotAssigned 替換為 None

// Column mapping, deprecated but Ok
val pk: Option[Long] = SQL("SELECT id FROM test WHERE name={n}").
  on('n -> "mine").as(get[Option[Long]].single)

// Assigned primary key as parameter
val idParam: Option[Long] = Some(2l)
val name1 = "Id"
SQL"INSERT INTO test(id, name) VALUES($idParam, $name1)".execute()

// Right parameter NotAssigned
val name2 = "NotAssigned"
SQL"INSERT INTO test(id, name) VALUES($None, $name2)".execute()

§Bootstrap

內建的 Bootstrap 欄位建構函式已棄用,並將在 Play 的未來版本中移除。

造成此變更的原因有幾個,其中一個原因是我們發現 Bootstrap 在不同版本之間的變動過於劇烈且過於頻繁,因此 Play 提供的任何內建支援很快就會過時,而且與目前的 Bootstrap 版本不相容。

另一個原因是目前的 Bootstrap CSS 類別需求無法單獨使用 Play 的欄位建構函式來實作,還需要自訂的輸入範本。

我們未來的觀點是,如果這項功能對社群有價值,可以建立一個提供特定於特定 Bootstrap 版本的獨立 Bootstrap 表單輔助範本組的第三方模組,提供比目前更好的使用者體驗。

§Session 超時

會話逾時設定項目 session.maxAge 以前是整數,定義為秒數。現在它是一個持續時間,因此可以使用 1h30m 等值來指定。不幸的是,如果未指定時間單位,則預設單位為毫秒,這表示以前將設定值 3600 視為一小時,但現在視為 3.6 秒。您需要更新設定檔以新增時間單位。

§Java JUnit 超類別

Java WithApplicationWithServerWithBrowser JUnit 測試超類別已修改為定義一個帶有 @Before 註解的方法。這表示,以前您必須透過定義來明確啟動應用程式

@Before
public void setUp() {
    start();
}

現在您不需要了。如果您需要提供自訂應用程式,您可以透過覆寫 provideFakeApplication 方法來執行此操作

@Override
protected Application provideFakeApplication() {
    return Helpers.fakeApplication(Helpers.inMemoryDatabase());
}

§會話和快閃記憶體暗示

Scala Controller 提供暗示的 SessionFlashLang 參數,這些參數採用暗示的 RequestHeader。這些參數存在於方便性中,因此範本例如可以採用暗示引數,而這些引數將自動在控制器中提供。這些名稱已變更,以避免這些參數名稱可能被具有相同名稱的應用程式局部變數遮蔽而產生的衝突。session 變成 request2Sessionflash 變成 flash2Sessionlang 變成 lang2Session。任何明確呼叫這些參數的程式碼因此都會中斷。

不建議您明確呼叫這些暗示方法,sessionflashlang 參數都可以在 RequestHeader 上使用,而使用 RequestHeader 屬性會讓它們在讀取程式碼時更清楚地了解它們的來源。建議您如果具有使用舊方法的程式碼,請修改它以直接存取 RequestHeader 上的對應屬性。

下一篇:Play 2.2


在此文件發現錯誤?此頁面的原始程式碼可在此處找到 here。在閱讀完 文件指南 後,請隨時提交拉取請求。有問題或建議要分享?前往 我們的社群論壇 與社群展開對話。