文档

§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 作为包含所有 Play 依赖项的 Activator 分发版进行分发。您可以从 Play 下载 页面下载此分发版。

如果您愿意,您也可以从 Activator 网站 下载 Activator 的最小 (1MB) 版本。在下载页面上查找“mini”分发版。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

通过在项目的根目录中声明 package.json 文件,可以使用 npm 以及 WebJars。来自 npm 包的资源将提取到与 WebJars 相同的 lib 文件夹中,因此从代码的角度来看,无需担心资源是来自 WebJar 还是来自 npm 包。


从您的角度来看,我们的目标是提供与 Play 以前版本的功能一致性。虽然内部发生了重大变化,但您的过渡应该很小。本节的其余部分将介绍 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 源文件和生成的源映射。它允许在现代 Web 浏览器中更轻松地调试。即使在生产模式下,此功能也是启用的。

插件的选项是

选项 描述
cleancss 使用 clean-css 压缩输出。
cleancssOptions 使用来自 https://github.com/GoalSmashers/clean-css 的 CLI 参数,将选项传递给 clean css。
color LESS 输出是否应彩色化
compress 通过删除一些空格来压缩输出。
ieCompat 执行 IE 兼容性检查。
insecure 允许从不安全的 https 主机导入。
maxLineLen 最大行长。
optimization 设置解析器的优化级别。
relativeUrls 将相对 URL 重写为基本 less 文件。
rootpath 为相对导入和 URL 中的 URL 重写设置 rootpath。
silent 抑制错误消息的输出。
sourceMap 输出 v3 源映射。
sourceMapFileInline 源映射是否应嵌入到输出文件中
sourceMapLessInline 是否将 less 代码嵌入到源映射中
sourceMapRootpath 将此路径添加到源映射文件名和 less 文件路径。
strictImports 导入是否应严格。
strictMath 需要括号。此选项可能默认为 true 并在将来删除。
strictUnits 所有单位是否应严格,或者是否允许混合单位。
verbose 详细。

有关更多信息,请参阅 插件文档

§Closure 编译器

Closure 编译器已被替换。它验证 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 插件提供(将在下面描述)。未来的目标是为不使用 RequireJS 的情况提供一个独立的 UglifyJS 2 插件。

§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 版本,资产控制器在请求时会优先使用它。此配置的 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 包含您的应用程序 js 文件的顶层目录。实际上,这是 rjs 读取的源文件夹。
baseUrl 相对于资产或公共文件夹的目录,其中包含 js 文件。将默认为“js”、“javascripts”或“.”,如果前两个找不到,则默认为“.”。
buildProfile 构建配置文件键 -> 值设置,除了 appBuildProfile 提供的默认值之外。此处的任何设置也将替换任何默认值。
dir 默认情况下,所有模块都位于此路径的相对位置。实际上,这是 rjs 的目标目录。
generateSourceMaps 默认情况下,会生成源映射。
mainConfig 默认情况下,使用“main”作为配置模块。
mainConfigFile 以上内容的完整路径。
mainModule 默认情况下,使用“main”作为模块。
modules 模块的 json 数组。
optimize 优化器的名称,默认为 uglify2。
paths 模块 ID 到构建路径和生产路径元组的 RequireJS 路径映射。默认情况下,所有 WebJar 库都可从 CDN 获取,并且它们的映射可以在此处找到(除非 cdn 设置为 None)。
preserveLicenseComments 是否保留注释。默认情况下,在源映射的情况下为 false(参见 https://requirejs.node.org.cn/docs/errors.html#sourcemapcomments))。
webJarCdns 用于定位 WebJars 的 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],因此建议用 Some[A] 替换 Id[A],用 None 替换 NotAssigned

// 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.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 控制器提供隐式 SessionFlashLang 参数,它们接受一个隐式 RequestHeader。这些参数是为了方便起见,例如,模板可以接受一个隐式参数,它们将在控制器中自动提供。这些参数的名称已更改,以避免与应用程序本地变量同名而导致的冲突。session 变成了 request2Sessionflash 变成了 flash2Sessionlang 变成了 lang2Session。任何显式调用这些参数的代码都会因此而失效。

不建议您显式调用这些隐式方法,sessionflashlang 参数在 RequestHeader 上都可用,使用 RequestHeader 属性可以更清楚地了解它们在代码中的来源。建议您如果代码使用旧方法,请修改代码以直接访问 RequestHeader 上的对应属性。

下一步: Play 2.2


发现此文档中的错误?此页面源代码可在 此处 找到。在阅读 文档指南 后,请随时贡献拉取请求。有任何问题或建议要分享?请访问 我们的社区论坛,与社区进行交流。