文档

§Play 2.5 迁移指南

本指南介绍如何从 Play 2.4 迁移到 Play 2.5。如果您需要从更早版本的 Play 迁移,则必须首先遵循 Play 2.4 迁移指南

除了本页面的信息外,还有一些主题的更详细的迁移信息

Lucidchart 还发布了一篇关于 从 Play 2.3.x 升级到 Play 2.5.x 的信息丰富的博客文章。

§如何迁移

在您可以在 sbt 中加载/运行 Play 项目之前,需要执行以下步骤来更新您的 sbt 构建。

§Play 升级

更新 project/plugins.sbt 中的 Play 版本号以升级 Play

addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.x")

其中 2.5.x 中的“x”是您要使用的 Play 的次要版本,例如 2.5.0

§sbt 升级到 0.13.11

虽然 Play 2.5 仍然可以使用 sbt 0.13.8,但我们建议您升级到最新的 sbt 版本 0.13.11。sbt 的 0.13.11 版本包含许多 改进和错误修复

更新您的 project/build.properties,使其读取

sbt.version=0.13.11

§Play Slick 升级

如果您的项目使用的是 Play Slick,则需要对其进行升级

libraryDependencies += "com.typesafe.play" %% "play-slick" % "2.0.0"

libraryDependencies ++= Seq(
  "com.typesafe.play" %% "play-slick" % "2.0.0",
  "com.typesafe.play" %% "play-slick-evolutions" % "2.0.0"
)

§Play Ebean 升级

如果您的项目使用 Play Ebean,您需要对其进行升级

addSbtPlugin("com.typesafe.sbt" % "sbt-play-ebean" % "3.0.0")

§ScalaTest + Play 升级

如果您的项目使用 ScalaTest + Play,您需要对其进行升级

libraryDependencies ++= Seq(
  "org.scalatestplus.play" %% "scalatestplus-play" % "1.5.1" % "test"
)

§Scala 2.10 支持已停止

Play 2.3 和 2.4 同时支持 Scala 2.10 和 2.11。Play 2.5 已停止支持 Scala 2.10,现在仅支持 Scala 2.11。这有几个原因

  1. Play 2.5 的内部代码广泛使用 scala-java8-compat 库,该库仅支持 Scala 2.11。scala-java8-compat 在许多 Scala 和 Java 8 类型之间进行转换,例如 Scala Future 和 Java CompletionStage。(您可能会发现此库对您的代码也很有用。)

  2. Play 的下一个版本可能会添加对 Scala 2.12 的支持。现在是 Play 转向 Scala 2.11 的时候了,这样即将到来的 2.12 过渡将更容易。

§如何迁移

Scala 和 Java 用户都必须配置 sbt 以使用 Scala 2.11。即使您的项目中没有 Scala 代码,Play 本身也使用 Scala,并且必须配置为使用正确的 Scala 库。

要在 sbt 中设置 Scala 版本,只需设置 scalaVersion 键,例如

scalaVersion := "2.11.8"

如果您只有一个项目构建,那么此设置可以放在 build.sbt 中的单独一行上。但是,如果您有多个项目构建,那么必须在每个项目上设置 scala 版本设置。通常,在多个项目构建中,您将有一些由每个项目共享的通用设置,这是放置设置的最佳位置,例如

def common = Seq(
  scalaVersion := "2.11.8"
)

lazy val projectA = (project in file("projectA"))
  .enablePlugins(PlayJava)
  .settings(common: _*)

lazy val projectB = (project in file("projectB"))
  .enablePlugins(PlayJava)
  .settings(common: _*)

§更改为 Logback 配置

作为删除 Play 对 Logback 的硬编码依赖项的更改的一部分 (参见亮点),Logback 配置使用的一个类必须移动到另一个包中。

§如何迁移

您需要更新您的 Logback 配置文件 (logback*.xml) 并将对旧的 play.api.Logger$ColoredLevel 的任何引用更改为新的 play.api.libs.logback.ColoredLevel 类。

更改后的新配置将如下所示

<conversionRule conversionWord="coloredLevel"
  converterClass="play.api.libs.logback.ColoredLevel" />

如果您使用编译时依赖项注入,则需要将应用程序加载器从使用 Logger.configure(...) 更改为以下内容

LoggerConfigurator(context.environment.classLoader).foreach { _.configure(context.environment) }

您可以在文档的 配置日志 部分找到有关如何使用不同的日志框架设置 Play 的更多详细信息。

§Play WS 升级到 AsyncHttpClient 2

Play WS 已升级到使用 AsyncHttpClient 2。这是一个重大升级,它使用 Netty 4.0。AHC 2.0 中的大多数更改都在幕后进行,但 AHC 有一些重大的重构,需要对 WS API 进行重大更改。

此外,还有一些小的更改。

§已弃用 GlobalSettings

作为 Play 逐步摆脱全局状态的努力的一部分,GlobalSettings 和应用程序 Global 对象已被弃用。有关更多详细信息,请参阅 Play 2.4 迁移指南,了解如何迁移以不再使用 GlobalSettings

§已移除 Plugins API

Plugins API 在 Play 2.4 中已弃用,并在 Play 2.5 中被移除。Plugins API 已被 Play 的依赖注入和模块系统取代,该系统提供了一种更简洁、更灵活的方式来构建可重用的组件。有关如何从插件迁移到依赖注入的详细信息,请参阅 Play 2.4 迁移指南

§使用 InjectedRoutesGenerator 生成的路由

路由现在使用依赖注入感知的 InjectedRoutesGenerator 生成,而不是之前的 StaticRoutesGenerator,该生成器假设控制器是单例对象。

要恢复到之前的行为(例如,如果您的代码中有“object MyController”),请将以下行添加到您的 build.sbt 文件中

routesGenerator := StaticRoutesGenerator

如果您使用的是 Build.scala 而不是 build.sbt,则需要导入 routesGenerator 设置键

import play.sbt.routes.RoutesCompiler.autoImport._

使用静态控制器和静态路由生成器并不被弃用,但建议您迁移到使用具有依赖注入的类。

§用依赖注入替换静态控制器

controllers.ExternalAssets 现在是一个类,没有静态等效项。controllers.Assetscontrollers.Default 也是类,虽然存在静态等效项,但建议您使用类版本。

§如何迁移

推荐的解决方案是为所有控制器使用类。InjectedRoutesGenerator 现在是默认值,因此路由文件中的控制器被假定为类而不是对象。

如果您仍然有静态控制器,可以使用 StaticRoutesGenerator(如上所述)并在 routes 文件中路由的前面添加 @ 符号,例如

GET  /assets/*file  @controllers.ExternalAssets.at(path = "/public", file)

§已弃用的 play.Play 和 play.api.Play 方法

以下方法在 play.Play 中已被弃用

同样,play.api.Play 中使用隐式 Application 并委托给 Application 的方法,例如 def classloader(implicit app: Application) 现在已弃用。

§如何迁移

这些方法委托给 play.Applicationplay.Environment - 使用这些方法的代码应该使用依赖注入来注入相关类。

您应该参考 Play 2.4 迁移指南 中的依赖注入组件列表来迁移内置的 Play 组件。

例如,以下代码在 Scala 中将环境和配置注入到控制器中

class HomeController @Inject() (environment: play.api.Environment,
    configuration: play.api.Configuration)
  extends Controller {

  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }

  def config = Action {
    Ok(configuration.underlying.getString("some.config"))
  }

  def count = Action {
    val num = environment.resource("application.conf").toSeq.size
    Ok(num.toString)
  }
}

§处理遗留组件

通常您使用的组件不需要依赖于整个应用程序,但有时您必须处理需要应用程序的遗留组件。您可以通过将应用程序注入到您的其中一个组件中来处理这种情况

class FooController @Inject() (appProvider: Provider[Application])
  extends Controller {
  implicit lazy val app = appProvider.get()
  def bar = Action {
    Ok(Foo.bar(app))
  }
}

请注意,在这种情况下,您通常希望使用 Provider[Application] 来避免循环依赖。

更好的是,您可以创建自己的 *Api 类,将静态方法转换为实例方法

class FooApi @Inject() (appProvider: Provider[Application]) {
  implicit lazy val app = appProvider.get()
  def bar = Foo.bar(app)
  def baz = Foo.baz(app)
}

这使您能够从 DI 带来的可测试性中获益,同时仍然使用使用全局状态的库。

§Content-Type 字符集更改

在 Play 2.5 之前,Play 会向某些未定义字符集参数的 Content-Type 添加 charset 参数,特别是 application/jsonapplication/x-www-form-urlencoded。现在,默认情况下,Content-Type 会在没有字符集的情况下发送。这适用于使用 WS 发送请求和从 Play 操作返回响应。如果您有一个非规范兼容的客户端或服务器,它要求您发送字符集参数,您可以显式设置 Content-Type 标头。

§Guice 注入器和 Guice 构建器更改

默认情况下,Guice 可以通过代理循环中的接口来解决循环依赖。由于循环依赖通常是代码异味,并且您也可以注入 Provider 来打破循环,因此我们选择在默认的 Guice 注入器上禁用此功能。其他 DI 框架也不太可能具有此功能,因此在编写 Play 模块时可能会导致问题。

现在,Guice 构建器(GuiceInjectorBuilderGuiceApplicationBuilder)上有四种新方法,用于自定义 Guice 如何注入您的类。
* disableCircularProxies:禁用上述代理接口以解决循环依赖的行为。要允许代理,请使用 disableCircularProxies(false)
* requireExplicitBindings:指示注入器仅注入在模块中显式绑定的类。在测试中验证绑定时可能很有用。
* requireAtInjectOnConstructors:要求使用 @Inject 注解的构造函数来实例化类。
* requireExactBindingAnnotations:禁用 Guice 中容易出错的功能,该功能可以在注入 @Named(“foo”) Foo 时用 @Named Foo 的绑定替换绑定。

§CSRF 更改

为了使 Play 的 CSRF 过滤器更能抵御浏览器插件漏洞和新扩展,CSRF 过滤器的默认配置已变得更加保守。更改包括

有一个新的配置选项可以绕过对具有某些标头的请求的新 CSRF 保护。此配置选项默认情况下为 Cookie 和 Authorization 标头启用,因此通常不使用会话身份验证的 REST 客户端仍然可以工作,而无需发送 CSRF 令牌。

但是,由于配置选项允许通过所有没有这些标头的请求,因此使用其他身份验证方案(NTLM、TLS 客户端证书)的应用程序将容易受到 CSRF 的攻击。这些应用程序应禁用配置选项,以便其经过身份验证的(无 cookie)请求受到 CSRF 过滤器的保护。

最后,添加了一个额外的选项,用于禁用 CORS 过滤器信任的来源的 CSRF 检查。请注意,CORS 过滤器必须在您的过滤器链中位于 CSRF 过滤器**之前**,才能使此功能生效!

可以通过在 application.conf 中添加以下配置来恢复 Play 的旧默认行为

play.filters.csrf {
  header {
    bypassHeaders {
      X-Requested-With = "*"
      Csrf-Token = "nocheck"
    }
    protectHeaders = null
  }
  bypassCorsTrustedOrigins = false
  method {
    whiteList = []
    blackList = ["POST"]
  }
  contentType.blackList = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"]
}

§获取 CSRF 令牌

以前,可以在任何操作中从 HTTP 请求中检索 CSRF 令牌。现在,您必须拥有 CSRF 过滤器或 CSRF 操作才能使 CSRF.getToken 工作。如果您没有使用过滤器,可以使用 Scala 中的 CSRFAddToken 操作或 Java 中的 AddCSRFToken 注解来确保会话中存在令牌。

此外,此版本修复了一个小错误,该错误会导致 CSRF 令牌为空(在模板助手中抛出异常),如果其签名无效。现在它将在同一个请求中重新生成,因此模板助手和 CSRF.getToken 仍然可以从令牌中获取。

有关更多详细信息,请阅读有关 JavaScala 的 CSRF 文档。

§Crypto 已弃用

从 Play 1.x 开始,Play 附带了一个 Crypto 对象,它提供了一些加密操作。Play 在内部使用它。Crypto 对象未在文档中提及,但在 scaladoc 中被提及为“加密实用程序”。

由于各种原因,提供加密实用程序作为便利措施已被证明不可行。在 2.5.x 中,Play 特定的功能已分解为 CookieSignerCSRFTokenSignerAESSigner 特性,并且 Crypto 单例对象已弃用。

§如何迁移

加密迁移将取决于您的用例,尤其是如果存在对加密原语的不安全构造。简而言之,如果可能,请使用 Kalium,否则请使用 Tink 或直接使用 JCA

有关更多详细信息,请参阅 加密迁移

§Netty 4 升级

Netty 已从 3.10 升级到 4.0。 这导致配置 Netty 通道选项的配置选项发生了变化。 完整的选项列表可以在 此处 查看。

§如何迁移

修改任何 play.server.netty.option 键以使用 ChannelOption 中定义的新键。 一些常用键的映射如下:

旧的 新的
play.server.netty.option.backlog play.server.netty.option.SO_BACKLOG
play.server.netty.option.child.keepAlive play.server.netty.option.child.SO_KEEPALIVE
play.server.netty.option.child.tcpNoDelay play.server.netty.option.child.TCP_NODELAY

§sendFilesendPathsendResource 方法的更改

Java (play.mvc.StatusHeader) 和 Scala (play.api.mvc.Results.Status) API 在之前具有以下行为:

API 方法 默认值
Scala play.api.mvc.Results.Status.sendResource 内联
Scala play.api.mvc.Results.Status.sendPath 附件
Scala play.api.mvc.Results.Status.sendFile 附件
Java play.mvc.StatusHeader.sendInputStream
Java play.mvc.StatusHeader.sendResource 内联
Java play.mvc.StatusHeader.sendPath 附件
Java play.mvc.StatusHeader.sendFile 内联

换句话说,它们在传递文件时混合了 inlineattachment 模式。 现在,在传递文件、路径和资源时,使用 inline 作为默认行为。 当然,您可以使用这些方法中提供的参数在两种模式之间进行切换。

下一步:流迁移指南


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