§Play 2.6 移行ガイド
これは、Play 2.5 から Play 2.6 に移行するためのガイドです。以前のバージョンのPlayから移行する必要がある場合は、まずPlay 2.5移行ガイドに従う必要があります。
§移行方法
sbt で Play プロジェクトをロード/実行する前に、次の手順を実行して sbt ビルドを更新する必要があります。
§Playのアップグレード
project/plugins.sbt
のPlayバージョン番号を更新して、Playをアップグレードします。
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.x")
2.6.x
の「x」は、使用するPlayのマイナーバージョンです。たとえば、2.6.0
です。
§sbtを0.13.15にアップグレード
Play 2.6では、少なくともsbt 0.13.15へのアップグレードが必要です。sbtの0.13.15リリースには、多くの改善とバグ修正が含まれています(sbt 0.13.13の変更も参照してください)。
sbt 1.xは、Play 2.6.6からサポートされています。他のsbtプラグインを使用している場合は、sbt 1.xと互換性のある新しいバージョンがあるかどうかを確認する必要がある場合があります。
更新するには、project/build.properties
を次のように変更します。
sbt.version=0.13.15
§Guice DIサポートが別のモジュールに移動
Play 2.6では、コアPlayモジュールにGuiceが含まれなくなりました。guice
をlibraryDependencies
に追加して、Guiceモジュールを構成する必要があります。
libraryDependencies += guice
§OpenIDサポートが別のモジュールに移動
Play 2.6では、コアPlayモジュールにplay.api.libs.openid
(Scala)およびplay.libs.openid
(Java)のOpenIDサポートが含まれなくなりました。これらのパッケージを使用するには、openId
をlibraryDependencies
に追加します。
libraryDependencies += openId
§Play JSONが別のプロジェクトに移動
Play JSONは、https://github.com/playframework/play-jsonでホストされている別のライブラリに移動されました。Play JSONにはPlayの他の部分への依存関係がないため、主な変更点は、sbtビルドでPlayImport
のjson
値が機能しなくなることです。代わりに、ライブラリを手動で指定する必要があります。
libraryDependencies += "com.typesafe.play" %% "play-json" % "2.6.0"
また、play-jsonはコアPlayライブラリとは別のリリースサイクルを持っているため、バージョンはPlayバージョンと同期しなくなりました。
§Play Iterateesが別のプロジェクトに移動
Play Iterateesは、https://github.com/playframework/play-iterateesでホストされている別のライブラリに移動されました。Play IterateesにはPlayの他の部分への依存関係がないため、主な変更点は、ライブラリを手動で指定する必要があることです。
libraryDependencies += "com.typesafe.play" %% "play-iteratees" % "2.6.1"
このプロジェクトには、IterateesをReactive Streamsと統合するサブプロジェクトもあります。次の依存関係も追加する必要がある場合があります。
libraryDependencies += "com.typesafe.play" %% "play-iteratees-reactive-streams" % "2.6.1"
注:ヘルパークラス
play.api.libs.streams.Streams
はplay-iteratees-reactive-streams
に移動し、現在はplay.api.libs.iteratee.streams.IterateeStreams
と呼ばれています。そのため、Iterateesの依存関係を追加し、必要に応じて新しいクラスを使用する必要がある場合があります。
最後に、Play Iterateesには別のバージョニングスキームがあるため、バージョンはPlayバージョンと同期しなくなりました。
§デフォルトのサーバーエンジンとしてのAkka HTTP
Playは、デフォルトのバックエンドとしてAkka-HTTPサーバーエンジンを使用するようになりました。何らかの理由で(たとえば、Nettyのネイティブトランスポートを使用している場合)、Nettyに戻す必要がある場合は、Nettyサーバーのドキュメントでその方法を参照してください。
詳しくは、Akka HTTPサーバーバックエンドをご覧ください。
§Akka HTTPサーバーのタイムアウト
Play 2.5.xには、デフォルトのサーバーバックエンドであったNettyサーバーのリクエストタイムアウト構成がありません。しかし、Akka HTTPには、アイドル状態の接続とリクエストの両方のタイムアウトがあります(詳細については、Akka HTTP設定ドキュメントを参照してください)。Akka HTTPドキュメントには次のように記載されています。
Akka HTTPには、悪意のある攻撃やプログラミングミスからサーバーを保護するためのさまざまな組み込みタイムアウトメカニズムが用意されています。
akka.http.server.idle-timeout
、akka.http.server.request-timeout
、およびakka.http.server.bind-timeout
のデフォルト値は、こちらで確認できます。Playには独自のタイムアウトを定義するための構成があるため、多くの503 Service Unavailable
が表示され始めた場合は、構成をアプリケーションにとってより適切な値に変更できます。たとえば、
play.server.http.idleTimeout = 60s
play.server.akka.requestTimeout = 40s
§Scala Mode
の変更
ScalaのMode
は、列挙型からケースオブジェクトの階層にリファクタリングされました。このリファクタリングのため、ほとんどのScalaコードは変更されません。ただし、JavaコードでScalaのMode
値にアクセスする場合は、次のように変更する必要があります。
// Consider this Java code
play.api.Mode scalaMode = play.api.Mode.Test();
次のように書き換える必要があります。
// Consider this Java code
play.api.Mode scalaMode = play.Mode.TEST.asScala();
JavaモードとScalaモード間の変換も簡単です。
// In your Java code
play.api.Mode scalaMode = play.Mode.DEV.asScala();
またはScalaコードで
play.Mode javaMode = play.api.Mode.Dev.asJava
また、play.api.Mode.Mode
は非推奨になったため、代わりにplay.api.Mode
を使用する必要があります。
§Writeable[JsValue]
の変更
以前は、デフォルトのScala Writeable[JsValue]
で暗黙的なCodec
を定義でき、異なる文字セットを使用して書き込むことができました。application/json
はテキストベースのコンテンツタイプのように機能しないため、これは問題になる可能性があります。Unicode文字セット(UTF-8
、UTF-16
、UTF-32
)のみを許可し、多くのテキストベースのコンテンツタイプのようにcharset
パラメーターを定義しません。
現在、デフォルトのWriteable[JsValue]
は暗黙的なパラメーターを受け取らず、常にUTF-8
で書き込みます。これにより、ほとんどのユーザーがJSONにUTF-8を使用したいと考えているため、ほとんどのケースがカバーされます。また、JSONをバイト配列に書き込むためのより効率的な組み込みメソッドを簡単に使用できます。
以前の動作に戻す必要がある場合は、目的のコーデックとコンテンツタイプに対して、play.api.http.Writeable.writeableOf_JsValue(codec, contentType)
を使用して任意のコーデックでWriteable
を定義できます。
§Scalaコントローラーの変更
過去の慣用的なPlayコントローラーには、グローバル状態が必要でした。それが主に必要だったのは、グローバルのplay.api.mvc.Action
オブジェクトとBodyParsers#parse
メソッドでした。
同じ構文を提供する、その状態を注入する新しい方法を備えた、いくつかの新しいコントローラークラスを提供しました。
- BaseController
:実装クラスによって提供できる抽象ControllerComponents
を備えたトレイト。
- AbstractController
:コンストラクターインジェクションを使用して注入できるControllerComponents
コンストラクターパラメーターを持つBaseController
を拡張する抽象クラス。
- InjectedController
:メソッドインジェクション(setControllerComponents
メソッドの呼び出し)を介してControllerComponents
を取得するBaseController
を拡張するトレイト。GuiceのようなランタイムDIフレームワークを使用している場合、これは自動的に行われます。
ControllerComponents
は、通常コントローラーで使用されるコンポーネントをまとめるためのものです。また、ControllerHelpers
を拡張し、独自のコンポーネントバンドルを注入して、アプリ独自のベースコントローラーを作成することもできます。Playでは、コントローラーが特定のトレイトを実装する必要はありません。
BaseController
は、通常望ましい動作である、グローバルオブジェクトではなくインジェクトされたインスタンスを参照するように Action
および parse
を設定することに注意してください。
以下は、AbstractController
を使用したコード例です。
class FooController @Inject() (components: ControllerComponents)
extends AbstractController(components) {
// Action and parse now use the injected components
def foo = Action(parse.json) {
Ok
}
}
そして BaseController
を使用した例です。
class FooController @Inject() (val controllerComponents: ControllerComponents) extends BaseController {
// Action and parse now use the injected components
def foo = Action(parse.json) {
Ok
}
}
そして InjectedController
を使用した例です。
class FooController @Inject() () extends InjectedController {
// Action and parse now use the injected components
def foo = Action(parse.json) {
Ok
}
}
InjectedController
は、setControllerComponents
メソッドを呼び出すことで、ControllerComponents
を取得します。このメソッドは JSR-330 準拠の依存性注入によって自動的に呼び出されます。コンパイル時注入で InjectedController
を使用することは推奨しません。コントローラーをユニットテストで手動で広範囲にテストする場合も、依存関係を隠蔽するため InjectedController
を避けることをお勧めします。
個々の依存関係を手動で渡したい場合は、代わりに依存関係や状態を持たない ControllerHelpers
を拡張することができます。以下に例を示します。
class Controller @Inject() (
action: DefaultActionBuilder,
parse: PlayBodyParsers,
messagesApi: MessagesApi
) extends ControllerHelpers {
def index = action(parse.text) { request =>
Ok(messagesApi.preferred(request)("hello.world"))
}
}
§Scala ActionBuilder および BodyParser の変更
Scala の ActionBuilder
トレイトは、ボディの型を型パラメータとして指定し、デフォルトのボディパーサーとして抽象メンバー parser
を追加するように変更されました。ActionBuilder を変更し、ボディパーサーを直接渡す必要があります。
play.api.mvc.Action
グローバルオブジェクトと BodyParsers#parse
は非推奨になりました。これらは、インジェクション可能なトレイトである DefaultActionBuilder
および PlayBodyParsers
でそれぞれ置き換えられます。コントローラー内では、これらは新しい BaseController
トレイトによって自動的に提供されます(上記の コントローラーの変更を参照)。
§Cookie
Java ユーザー向けに、新しいクッキーを作成するには、play.mvc.Http.Cookie.builder
を使用することをお勧めします。以下に例を示します。
Http.Cookie cookie = Cookie.builder("color", "blue")
.withMaxAge(3600)
.withSecure(true)
.withHttpOnly(true)
.withSameSite(SameSite.STRICT)
.build();
これはプレーンなコンストラクタ呼び出しよりも可読性が高く、将来クッキー属性を追加/削除する場合でもソース互換性があります。
§セッションおよびフラッシュに有効な SameSite 属性
クッキーは、CSRF を防止するために使用できる追加の SameSite
属性を持つことができるようになりました。可能な状態は 3 つあります。
SameSite
なし:これは、そのドメインへのすべてのリクエストに対してクッキーが送信されることを意味します。SameSite=Strict
:これは、クッキーがクロスサイトリクエストではなく、同じサイトからのリクエスト(サイト上の別のページから来たリクエスト)に対してのみ送信されることを意味します。SameSite=Lax
:これは、クッキーがトップレベルナビゲーションとしてクロスサイトリクエストに対して送信されますが、それ以外の場合は同じサイトのリクエストに対してのみ送信されることを意味します。これにより、ほとんどのサイトで正しい動作が実行されますが、ポップアップウィンドウを起動することによって実行されるような特定のタイプの攻撃を防ぐことはできません。
さらに、セッションおよびフラッシュクッキーは、デフォルトで SameSite=Lax
を使用するように変更しました。これは構成を使用して調整できます。たとえば、
play.http.session.sameSite = null // no same-site for session
play.http.flash.sameSite = "strict" // strict same-site for flash
注: この機能は現在、多くのブラウザーでサポートされていません。したがって、この機能に依存すべきではありません。現在、SameSite をサポートしている主要なブラウザーは Chrome と Opera のみです。
§__Host および __Secure 接頭辞
__Host および __Secure クッキー名接頭辞のサポートも追加しました。
これは、クッキー名にこれらの接頭辞を使用している場合にのみ影響します。そうである場合、Play は、適切な属性が設定されていない場合、これらのクッキーをシリアル化およびデシリアル化するときに警告し、自動的に設定します。警告を削除するには、これらの接頭辞をクッキーに使用するのをやめるか、次のように属性を必ず設定してください。
__Host-
で始まる名前のクッキーは、Path=/
およびSecure
属性を設定する必要があります。__Secure-
で始まる名前のクッキーは、Secure
属性を設定する必要があります。
§アセット
§コンパイル時 DI によるアセットのバインディング
コンパイル時 DI を使用している場合は、controllers.AssetsComponents
をミックスインし、それを使用して assets: Assets
コントローラーインスタンスを取得する必要があります。
class MyComponents(context: Context) extends BuiltInComponentsFromContext(context) with AssetsComponents {
lazy val router = new Routes(httpErrorHandler, assets)
}
既存の lazy val assets: Assets
がある場合は、削除できます。
§アセット設定
既存のユーザー向け API は変更されていませんが、アセットを見つけたり、構成でアセットディレクトリを設定したりするには、AssetsFinder
API に移行することをお勧めします。
play.assets {
path = "/public"
urlPrefix = "/assets"
}
次に、ルートで以下のように記述できます。
# prefix must match `play.assets.urlPrefix`
GET /assets/*file controllers.Assets.at(file)
GET /versionedAssets/*file controllers.Assets.versioned(file)
引数リストの先頭にアセットパスを指定する必要はなくなりました。これは構成から読み込まれるようになったためです。
次に、テンプレートで AssetsFinder#path
を使用して、アセットの最終パスを見つけることができます。
@(assets: AssetsFinder)
<img alt="hamburger" src="@assets.path("images/hamburger.jpg")">
Assets.versioned
でリバースルートを引き続き使用できますが、提供されたアセット名を最終的なアセット名に変換するには、一部のグローバル状態が必要であり、複数のアプリケーションを同時に実行したい場合は問題になる可能性があります。
§フォームの変更
Play 2.6 以降、POST
、PUT
、または PATCH
リクエストと組み合わせて bindFromRequest()
を使用する場合、クエリ文字列パラメーターはフォームインスタンスにバインドされなくなります。
すでに 2.5 で非推奨になっていた静的メソッド(例:DynamicForm.form()
)は、このリリースで削除されました。引き続き使用する場合は、移行方法の詳細について Play 2.5 移行ガイドを参照してください。
§Java フォームの変更
play.data.Form
インスタンスの errors()
メソッドは非推奨になりました。代わりに、単純な List<ValidationError>
を返す allErrors()
を使用する必要があります。Map<String,List<ValidationError>>
ではありません。Play 2.6 より前は .errors().get("key")
を呼び出していたところは、.errors("key")
を呼び出すだけで済みます。
今後は、フォームクラス内に実装された validate
メソッド(通常はクロスフィールド検証に使用される)は、クラスレベルの制約の一部です。このような制約の使用方法の詳細については、高度な検証ドキュメントを参照してください。
既存の validate
メソッドは、影響を受けるフォームクラスに @Validate
を注釈し、validate メソッドの戻り型に応じて、該当する型引数を持つ Validatable
インターフェースを実装することで、簡単に移行できます(すべて play.data.validation.Constraints
で定義されています)。
戻り型 | 実装するインターフェース |
---|---|
String |
Validatable<String> |
ValidationError |
Validatable<ValidationError> |
List<ValidationError> |
Validatable<List<ValidationError>> |
Map<String,List<ValidationError>> (サポートされなくなりました。代わりに List を使用してください) |
Validatable<List<ValidationError>> |
たとえば、次のような既存のフォームは、
public class MyForm {
//...
public String validate() {
//...
}
}
次のように変更する必要があります。
import play.data.validation.Constraints.Validate;
import play.data.validation.Constraints.Validatable;
@Validate
public class MyForm implements Validatable<String> {
//...
@Override
public String validate() {
//...
}
}
注意:「古い」
validate
メソッドは、他のすべての制約が成功した後にのみ呼び出されました。ただし、デフォルトでは、クラスレベルの制約は、成功したか失敗したかに関係なく、他の制約注釈と同時に呼び出されます。制約間の順序を(も)定義するには、制約グループを使用できます。
§JPA 移行の注意事項
JPA 移行の注意事項を参照してください。
§I18n 移行の注意事項
I18N API 移行を参照してください。
§キャッシュ API 移行の注意事項
キャッシュ API 移行を参照してください。
§Java 構成 API 移行の注意事項
Java 構成移行を参照してください。
§Scala 構成 API
Scala の play.api.Configuration
API には、ConfigLoader
を使用して任意の型をロードできる新しいメソッドが追加されました。これらの新しいメソッドは、構成ファイルに構成キーが存在することを前提としています。たとえば、次の古いコードは、
val myConfig: String = configuration.getString("my.config.key").getOrElse("default")
次のように変更する必要があります。
val myConfig: String = configuration.get[String]("my.config.key")
また、値 "default" は、my.config.key = default
として構成に設定する必要があります。
または、デフォルト値を取得するためにコード内でカスタムロジックが必要な場合は、構成ファイルでデフォルトを null に設定し(my.config.key = null
)、Option[T]
を読み取ることができます。
val myConfigOption: Option[String] = configuration.get[Option[String]]("my.config.key")
val myConfig: String = myConfigOption.getOrElse(computeDefaultValue())
また、古い play.api.Configuration
には、getBooleanList
のような Java 型を返すメソッドがいくつかあります。可能であれば、代わりに Scala バージョンの get[Seq[Boolean]]
を使用することをお勧めします。それが不可能な場合は、underlying
Config オブジェクトにアクセスして、そこから getBooleanList
を呼び出すことができます。
既存のメソッドの非推奨メッセージは、各メソッドを移行する方法も説明しています。 play.api.Configuration
の適切な使用方法の詳細については、Scala 構成ドキュメントを参照してください。
§Play JSON API の変更
§JSON 配列インデックスルックアップ
Scala play-json API を使用している場合、JsLookup
暗黙クラスの動作方法に小さな変更がありました。たとえば、次のようなコードがある場合、
val bar = (jsarray(index) \ "bar").as[Bar]
ここで、index
は配列インデックスで、jsarray
は JsArray
である場合、次は次のように記述する必要があります。
val bar = (jsarray \ index \ "bar").as[Bar]
これは、Scalaにおける他のコレクションのインデックス処理の動作と、JsArray
のインデックス処理の動作を一致させるために行われました。jsarray(index)
メソッドは、指定されたインデックスの値が存在すればその値を返し、存在しなければ例外をスローするようになりました。
また、play-json APIにも小さな変更があり、play.api.data.validation.ValidationError
がplay.api.libs.json.JsonValidationError
に変更されました。たとえば、次のようなコードがあった場合
ValidationError("Validation Error")
ここで、「Validation Error」がメッセージである場合、現在は次のように記述する必要があります。
JsonValidationError("Validation Error")
§削除されたAPI
§削除されたCrypto API
Crypto APIは、非推奨となっていたクラスであるplay.api.libs.Crypto
、play.libs.Crypto
、およびAESCTRCrypter
を削除しました。Crypto
へのCSRF参照はCSRFTokenSigner
に置き換えられました。セッションクッキーのCrypto
への参照はCookieSigner
に置き換えられました。詳細については、CryptoMigration25を参照してください。
§Akka
の非推奨メソッドの削除
非推奨の静的メソッドであるplay.libs.Akka.system
およびplay.api.libs.concurrent.Akka.system
が削除されました。依存性注入を使用してActorSystem
のインスタンスを取得し、アクターシステムにアクセスしてください。
Scalaの場合
class MyComponent @Inject() (system: ActorSystem) {
}
Javaの場合
public class MyComponent {
private final ActorSystem system;
@Inject
public MyComponent(ActorSystem system) {
this.system = system;
}
}
また、Play 2.6.xでは、Akka 2.5.xリリースシリーズを使用するようになりました。Akkaの2.4.xから2.5.xへの移行ガイドを参照し、必要に応じて自分のコードを適応させてください。
§削除されたYaml API
Play内部でplay.libs.Yaml
を使用しなくなったため、これを削除しました。Play YAML統合のサポートがまだ必要な場合は、build.sbt
にsnakeyaml
を追加する必要があります。
libraryDependencies += "org.yaml" % "snakeyaml" % "1.17"
そして、次のラッパーをコード内に作成します。
public class Yaml {
private final play.Environment environment;
@Inject
public Yaml(play.Environment environment) {
this.environment = environment;
}
/**
* Load a Yaml file from the classpath.
*/
public Object load(String resourceName) {
return load(
environment.resourceAsStream(resourceName),
environment.classLoader()
);
}
/**
* Load the specified InputStream as Yaml.
*
* @param classloader The classloader to use to instantiate Java objects.
*/
public Object load(InputStream is, ClassLoader classloader) {
org.yaml.snakeyaml.Yaml yaml = new org.yaml.snakeyaml.Yaml(new CustomClassLoaderConstructor(classloader));
return yaml.load(is);
}
}
またはScalaの場合
class Yaml @Inject()(environment: play.api.Environment) {
def load(resourceName: String) = {
load(environment.resourceAsStream(resourceName), environment.classLoader)
}
def load(inputStream: InputStream, classLoader: ClassLoader) = {
new org.yaml.snakeyaml.Yaml(new CustomClassLoaderConstructor(classloader)).load(inputStream)
}
}
Playの代替DIライブラリに明示的に依存している場合、または独自のカスタムアプリケーションローダーを定義している場合は、変更は必要ありません。
PlayのDIサポートを提供するライブラリは、play.application.loader
構成キーを定義する必要があります。外部DIライブラリが提供されていない場合、ApplicationLoader
を指すように設定しない限り、Playは起動を拒否します。
§非推奨のplay.Routes
の削除
JavaScriptルーターを作成するために使用されていた非推奨のplay.Routes
クラスが削除されました。新しいJavaまたはScalaのヘルパーを使用する必要があります。
§削除されたライブラリ
デフォルトのPlayディストリビューションを少し小さくするために、いくつかのライブラリを削除しました。以下のライブラリはPlay 2.6ではもはや依存関係ではなくなったため、使用する場合は手動でビルドに追加する必要があります。
§Joda-Timeの削除
java.time
APIの使用を推奨するため、Playのコアからjoda-timeのサポートを削除しています。
PlayのScalaフォームライブラリには、いくつかのJodaフォーマットが含まれていました。移行したくない場合は、build.sbt
にjodaForms
モジュールを追加できます。
libraryDependencies += jodaForms
そして、対応するオブジェクトをインポートします。
import play.api.data.JodaForms._
play-jsonでJodaのサポートが必要な場合は、次の依存関係を追加できます。
libraryDependencies += "com.typesafe.play" %% "play-json-joda" % playJsonVersion
ここで、playJsonVersion
は使用したいplay-jsonのバージョンです。Play 2.6.xはplay-json 2.6.xと互換性があるはずです。play-jsonは現在、独立したプロジェクトであることに注意してください(後述)。
import play.api.libs.json.JodaWrites._
import play.api.libs.json.JodaReads._
§Joda-Convertの削除
Playはjoda-convert
を内部的にいくつか使用していました。プロジェクトで使用している場合は、build.sbt
に追加する必要があります。
libraryDependencies += "org.joda" % "joda-convert" % "1.8.1"
§XercesImplの削除
XML処理のために、PlayはXerces XMLライブラリを使用していました。最近のJVMはXercesを参照実装として使用しているため、削除しました。プロジェクトが外部パッケージに依存している場合は、単にbuild.sbt
に追加できます。
libraryDependencies += "xerces" % "xercesImpl" % "2.11.0"
§H2の削除
Playの以前のバージョンでは、H2データベースがプリパッケージされていました。ただし、Playのコアを小さくするために、削除しました。H2を使用する場合は、build.sbt
に追加できます。
libraryDependencies += "com.h2database" % "h2" % "1.4.193"
テストでのみ使用していた場合は、Test
スコープを使用することもできます。
libraryDependencies += "com.h2database" % "h2" % "1.4.193" % Test
依存関係を追加すると、H2ブラウザは引き続き機能します。
§snakeyamlの削除
Playはplay.libs.Yaml
を削除したため、snakeyaml
への依存関係が削除されました。まだ使用している場合は、build.sbt
に追加してください。
libraryDependencies += "org.yaml" % "snakeyaml" % "1.17"
Yaml APIの削除に関する注意事項も参照してください。
§Tomcat-servlet-apiの削除
Playはtomcat-servlet-api
が不要になったため削除しました。まだ使用している場合は、build.sbt
に追加してください。
libraryDependencies += "org.apache.tomcat" % "tomcat-servlet-api" % "8.0.33"
§fork-runの削除
sbt-fork-run-plugin
は、寿命が尽きたActivatorユーティリティにのみ必要であったため、生成されなくなります。2.6では解決されなくなるため、完全に削除しても問題ありません。
§リクエスト属性
すべてのリクエストオブジェクトに属性が含まれるようになりました。リクエスト属性は、リクエストタグの代替です。タグは非推奨になったため、属性にアップグレードする必要があります。属性はタグよりも強力です。タグは文字列の保存のみをサポートしていましたが、属性を使用するとオブジェクトをリクエストに保存できます。
§リクエストタグの非推奨
タグは非推奨になっているため、タグの使用から属性の使用に移行する必要があります。移行はかなり簡単に行えるはずです。
最も簡単な移行パスは、タグからString
型の属性に移行することです。
Javaの場合(移行前)
// Getting a tag from a Request or RequestHeader
String userName = req.tags().get("userName");
// Setting a tag on a Request or RequestHeader
req.tags().put("userName", newName);
// Setting a tag with a RequestBuilder
Request builtReq = requestBuilder.tag("userName", newName).build();
Javaの場合(移行後)
class Attrs {
public static final TypedKey<String> USER_NAME = TypedKey.<String>create("userName");
}
// Getting an attribute from a Request or RequestHeader
String userName = req.attrs().get(Attrs.USER_NAME);
String userName = req.attrs().getOptional(Attrs.USER_NAME);
// Setting an attribute on a Request or RequestHeader
Request newReq = req.withTags(req.tags().put(Attrs.USER_NAME, newName));
// Setting an attribute with a RequestBuilder
Request builtReq = requestBuilder.attr(Attrs.USER_NAME, newName).build();
Scalaの場合(移行前)
// Getting a tag from a Request or RequestHeader
val userName: String = req.tags("userName")
val optUserName: Option[String] = req.tags.get("userName")
// Setting a tag on a Request or RequestHeader
val newReq = req.copy(tags = req.tags.updated("userName", newName))
Scalaの場合(移行後)
object Attrs {
val UserName: TypedKey[String] = TypedKey("userName")
}
// Getting an attribute from a Request or RequestHeader
val userName: String = req.attrs(Attrs.UserName)
val optUserName: [String] = req.attrs.get(Attrs.UserName)
// Setting an attribute on a Request or RequestHeader
val newReq = req.addAttr(Attrs.UserName, newName)
ただし、適切であれば、String
タグを非String
値を持つ属性に変換することをお勧めします。タグを非String
オブジェクトに変換すると、いくつかの利点があります。まず、コードがよりタイプセーフになります。これにより、コードの信頼性が向上し、理解しやすくなります。次に、属性に保存するオブジェクトには複数のプロパティを含めることができるため、複数のタグを1つの値に集約できます。第3に、タグを属性に変換すると、String
からの値のエンコードとデコードが不要になり、パフォーマンスが向上する可能性があります。
class Attrs {
public static final TypedKey<User> USER = TypedKey.<User>create("user");
}
Scalaの場合(移行後)
object Attrs {
val UserName: TypedKey[User] = TypedKey("user")
}
§FakeRequest.withCookies
の呼び出しは、Cookies
ヘッダーを更新しなくなりました
リクエストクッキーはリクエスト属性に保存されるようになりました。以前は、リクエストのCookie
ヘッダーString
に保存されていました。これには、クッキーが変更されるたびに、クッキーをヘッダーにエンコードおよびデコードする必要がありました。
クッキーがリクエスト属性に保存されるようになったため、クッキーを更新すると、新しいクッキー属性は変更されますが、Cookie
HTTPヘッダーは変更されません。これは、withCookies
を呼び出すとヘッダーが更新されるという事実に依存している場合にのみ、テストに影響します。
古い動作がまだ必要な場合は、Cookies.encodeCookieHeader
を使用してCookie
オブジェクトをHTTPヘッダーに変換し、そのヘッダーをFakeRequest.withHeaders
で保存することができます。
§play.api.mvc.Security.username
(Scala API)、session.username
の変更
play.api.mvc.Security.username
(Scala API)、session.username
構成キー、および依存するアクションヘルパーは非推奨です。Security.username
は構成からsession.username
キーを取得するだけであり、ユーザー名を取得するために使用されるセッションキーを定義していました。静的機能を必要としたため削除されましたが、同じまたは同様の動作を自分で実装するのは非常に簡単です。
configuration.get[String]("session.username")
を使用して、ユーザー名セッションキーを構成から自分で読み取ることができます。
Authenticated(String => EssentialAction)
メソッドを使用している場合は、同様の処理を行う独自のアクションを簡単に作成できます。
def AuthenticatedWithUsername(action: String => EssentialAction) =
WithAuthentication[String](_.session.get(UsernameKey))(action)
ここで、UsernameKey
は、ユーザー名に使用するセッションキーを表します。
§リクエストセキュリティ(Java API)ユーザー名プロパティが属性になりました
Javaリクエストオブジェクトには、JavaアクションにSecurity.Authenticated
アノテーションが追加されたときに設定されるusername
プロパティが含まれています。Play 2.6では、ユーザー名プロパティは非推奨になりました。ユーザー名プロパティメソッドは、ユーザー名をSecurity.USERNAME
属性に保存するように更新されました。コードを更新して、Security.USERNAME
属性を直接使用する必要があります。今後のPlayのバージョンでは、ユーザー名プロパティを削除します。
この変更の理由は、ユーザー名プロパティがSecurity.Authenticated
アノテーションの特別なケースとして提供されていたためです。属性が導入されたため、特別なケースは必要なくなりました。
既存のJavaコード
// Set the username
Request reqWithUsername = req.withUsername("admin");
// Get the username
String username = req1.username();
// Set the username with a builder
Request reqWithUsername = new RequestBuilder().username("admin").build();
更新されたJavaコード
import play.mvc.Security.USERNAME;
// Set the username
Request reqWithUsername = req.withAttr(USERNAME, "admin");
// Get the username
String username = req1.attr(USERNAME);
// Set the username with a builder
Request reqWithUsername = new RequestBuilder().putAttr(USERNAME, "admin").build();
§ルータータグが属性になりました
Router.Tags.*
タグを使用した場合は、コードを更新して、新しいRouter.Attrs.HandlerDef
(Scala)またはRouter.Attrs.HANDLER_DEF
(Java)属性を使用する必要があります。既存のタグはまだ利用できますが、非推奨であり、今後のPlayのバージョンで削除されます。
この新しい属性には、タグに含まれているすべての情報を持つHandlerDef
オブジェクトが含まれています。現在のタグはすべて、HandlerDef
オブジェクトのフィールドに対応しています。
Javaタグ名 | Scalaタグ名 | HandlerDef メソッド |
---|---|---|
ROUTE_PATTERN |
RoutePattern |
path |
ROUTE_VERB |
RouteVerb |
verb |
ROUTE_CONTROLLER |
RouteController |
controller |
ROUTE_ACTION_METHOD |
RouteActionMethod |
method |
ROUTE_COMMENTS |
RouteComments |
comments |
注意:この変更の一環として、
HandlerDef
オブジェクトは内部パッケージであるplay.core.routing
から、パブリックAPIパッケージであるplay.api.routing
に移動されました。
§play.api.libs.concurrent.Execution
は非推奨です
play.api.libs.concurrent.Execution
クラスは、内部でグローバルな可変状態を使用して「現在の」アプリケーションのExecutionContextを取得していたため、非推奨になりました。
以前と同じ暗黙的な動作を指定する場合は、依存性注入を使用してコンストラクターで暗黙的に実行コンテキストを渡す必要があります。
class MyController @Inject()(implicit ec: ExecutionContext) {
}
または、コンパイル時依存性注入を使用している場合はBuiltInComponentsから渡します。
class MyComponentsFromContext(context: ApplicationLoader.Context)
extends BuiltInComponentsFromContext(context) {
val myComponent: MyComponent = new MyComponent(executionContext)
}
ただし、一般的なケースでも実行コンテキストをインポートしたくない正当な理由がいくつかあります。一般的なケースでは、アプリケーションの実行コンテキストは、アクションのレンダリング、およびブロッキングAPI呼び出しやI/Oアクティビティを伴わないCPUバウンドのアクティビティの実行に適しています。データベースを呼び出したり、ネットワーク呼び出しを行ったりする場合は、独自のカスタム実行コンテキストを定義する必要があります。
カスタム実行コンテキストを作成する推奨される方法は、CustomExecutionContext
を使用することです。これは、Akkaディスパッチャーシステム(java / scala)を使用しており、構成を通じて実行者を定義できます。
独自の実行コンテキストを使用するには、application.conf
ファイル内のディスパッチャーへの完全パスを持つCustomExecutionContext
抽象クラスを拡張します。
import play.api.libs.concurrent.CustomExecutionContext
class MyExecutionContext @Inject()(actorSystem: ActorSystem)
extends CustomExecutionContext(actorSystem, "my.dispatcher.name")
import play.libs.concurrent.CustomExecutionContext;
class MyExecutionContext extends CustomExecutionContext {
@Inject
public MyExecutionContext(ActorSystem actorSystem) {
super(actorSystem, "my.dispatcher.name");
}
}
次に、必要に応じてカスタム実行コンテキストを注入します。
class MyBlockingRepository @Inject()(implicit myExecutionContext: MyExecutionContext) {
// do things with custom execution context
}
カスタムスレッドプール構成の詳細については、スレッドプールのページを参照してください。また、CustomExecutionContext
の使用については、JavaAsync / ScalaAsyncを参照してください。
§play.api.testヘルパーの変更
以下の非推奨のテストヘルパーが2.6.xで削除されました。
play.api.test.FakeApplication
はplay.api.inject.guice.GuiceApplicationBuilder
に置き換えられました。play.api.test.Helpers.route(request)
はplay.api.test.Helpers.routes(app, request)
メソッドに置き換えられました。play.api.test.Helpers.route(request, body)
は、play.api.test.Helpers.routes(app, request, body)
メソッドに置き換えられました。
§Java API
play.test.FakeRequest
はRequestBuilder
に置き換えられました。play.test.FakeApplication
はplay.inject.guice.GuiceApplicationBuilder
に置き換えられました。play.test.Helpers.fakeApplication
から新しいApplication
を作成できます。play.test.WithApplication
において、非推奨のprovideFakeApplication
メソッドは削除されました。代わりにprovideApplication
メソッドを使用する必要があります。
§テンプレートヘルパーの変更
views/helper/requireJs.scala.html
の requireJs
テンプレートヘルパーは、設定にアクセスするために Play.maybeApplication
を使用していました。
requireJs
テンプレートヘルパーに、ヘルパーのminify版を使用するかどうかを示す追加パラメータ isProd
が追加されました。
@requireJs(core = routes.Assets.at("javascripts/require.js").url, module = routes.Assets.at("javascripts/main").url, isProd = true)
§ファイル拡張子とMIMEタイプのマッピングの変更
ファイル拡張子からMIMEタイプへのマッピングは reference.conf
に移動し、play.http.fileMimeTypes
設定の下で完全に設定を通じて扱われるようになりました。以前は、リストは play.api.libs.MimeTypes
の下でハードコードされていました。
play.http.fileMimeTypes
の設定は、三重引用符で囲まれた単一の文字列として定義されていることに注意してください。これは、いくつかのファイル拡張子(例:c++
)がHOCONを破壊する構文を持っているためです。
カスタムMIMEタイプを追加するには、HOCON文字列値連結を使用してください。
play.http.fileMimeTypes = ${play.http.fileMimeTypes} """
foo=text/bar
"""
追加のMIMEタイプのために mimetype.foo=text/bar
として定義された設定を許可する構文があります。これは非推奨であり、上記の構成を使用することをお勧めします。
§Java API
Http.Context.current().fileMimeTypes()
メソッドが内部的に Results.sendFile
やファイル拡張子からコンテンツタイプをルックアップするその他のメソッドに提供されています。移行は必要ありません。
§Scala API
play.api.libs.MimeTypes
クラスは play.api.http.FileMimeTypes
インターフェースに変更され、実装は play.api.http.DefaultFileMimeTypes
に変更されました。
ファイルを送信するすべての結果は、暗黙的に FileMimeTypes
を受け取るようになりました。例えば:
implicit val fileMimeTypes: FileMimeTypes = ...
Ok(file) // <-- takes implicit FileMimeTypes
FileMimeTypes
の暗黙的なインスタンスは、便利なバインディングを提供するために、BaseController
(およびそのサブクラスである AbstractController
、サブトレイトである InjectedController
) によって ControllerComponents
クラスを通じて提供されます。
class SendFileController @Inject() (cc: ControllerComponents) extends AbstractController(cc) {
def index() = Action { implicit request =>
val file = readFile()
Ok(file) // <-- takes implicit FileMimeTypes
}
}
ユニットテストで完全に構成された FileMimeTypes
インスタンスを直接取得することもできます。
val httpConfiguration = new HttpConfigurationProvider(Configuration.load(Environment.simple)).get
val fileMimeTypes = new DefaultFileMimeTypesProvider(httpConfiguration.fileMimeTypes).get
または、カスタムインスタンスを取得することもできます。
val fileMimeTypes = new DefaultFileMimeTypesProvider(FileMimeTypesConfiguration(Map("foo" -> "text/bar"))).get
§デフォルトフィルター
Playには、設定を通じて定義されたデフォルトの有効なフィルターセットが付属しています。プロパティ play.http.filters
がnullの場合、デフォルトは play.api.http.EnabledFilters
になり、play.filters.enabled
設定プロパティで完全修飾クラス名によって定義されたフィルターをロードします。
Play自体では、play.filters.enabled
は空のリストです。ただし、filtersライブラリは、PlayFilters
という名前のAutoPluginとしてsbtに自動的にロードされ、次の値を play.filters.enabled
プロパティに追加します。
play.filters.csrf.CSRFFilter
play.filters.headers.SecurityHeadersFilter
play.filters.hosts.AllowedHostsFilter
これは、新しいプロジェクトでは、CSRF保護 (ScalaCsrf / JavaCsrf)、SecurityHeaders、および AllowedHostsFilter がすべて自動的に定義されることを意味します。
§デフォルトフィルターの効果
デフォルトフィルターは、プロジェクトに「デフォルトで安全な」構成を提供するように設定されています。
これらのフィルターは有効にしたままにする必要があります。これらはアプリケーションのセキュリティを高めます。
既存のプロジェクトでこれらのフィルターを有効にしていなかった場合、いくつかの構成が必要になり、関連するエラーや失敗に慣れていない可能性があります。移行を支援するために、各フィルター、その機能、および必要な構成について説明します。
§CSRFFilter
CSRFフィルターについては、ScalaCsrf と JavaCsrf で説明されています。POSTリクエストでチェックされるCSRFトークンをフォームに追加することにより、クロスサイトリクエストフォージェリ攻撃から保護します。
§デフォルトで有効になっている理由
CSRFは、実装にほとんどスキルを必要としない非常に一般的な攻撃です。https://github.com/Manc/play-scala-csrf で、Playを使用したCSRF攻撃の例を見ることができます。
§どのような変更が必要ですか?
CSRF.formField
などのCSRFフォームヘルパーを使用していない既存のプロジェクトから移行する場合、CSRFフィルターからのPUTおよびPOSTリクエストで「403 Forbidden」が表示される場合があります。
フォームテンプレートに CSRF.formField
を追加すると、エラーが解決されます。AJAXを使用してリクエストを作成する場合は、CSRFトークンをHTMLページに配置し、Csrf-Token
ヘッダーを使用してリクエストに追加できます。
この動作を確認するには、logback.xml
に <logger name="play.filters.csrf" value="TRACE"/>
を追加してください。
また、PlayでSameSite Cookieを有効にすると、CSRF攻撃に対する追加の防御を提供できます。
§SecurityHeadersFilter
SecurityHeadersFilter は、リクエストにHTTPヘッダーを追加することにより、クロスサイトスクリプティングおよび クリックジャッキング攻撃を防ぎます。
§デフォルトで有効になっている理由
ブラウザベースの攻撃は非常に一般的であり、セキュリティヘッダーはそれらの攻撃を阻止するのに役立つ多層防御を提供できます。
§どのような変更が必要ですか?
デフォルトの「Content-Security-Policy」設定は非常に厳格であり、最も便利な設定を見つけるために実験する必要がある可能性があります。Content-Security-Policy設定は、ブラウザでのJavaScriptとリモートフレームの表示方法を変更します。Content-Security-Policyヘッダーを変更するまで、埋め込みのJavaScriptまたはCSSはWebページにロードされません。
有効にしたくない場合は、次のようにContent-Security-Policyを無効にできます。
play.filters.headers.contentSecurityPolicy=null
CSP-Useful は、一般的なコンテンツセキュリティポリシーに関する優れたリソースです。リクエストごとにカスタムCSP nonceを追加するなど、埋め込みJavaScriptに対する他の潜在的なソリューションがあることに注意してください。
他のヘッダーはそれほど侵入的ではなく、一般的なWebサイトでは問題を引き起こす可能性は低いですが、シングルページアプリケーションではCookieやレンダリングの問題を引き起こす可能性があります。Mozillaには、URLにヘッダー名を使用し、各ヘッダーについて詳しく説明するドキュメントがあります。たとえば、X-Frame-Optionsの場合は、https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Optionsにアクセスしてください。
play.filters.headers {
# The X-Frame-Options header. If null, the header is not set.
frameOptions = "DENY"
# The X-XSS-Protection header. If null, the header is not set.
xssProtection = "1; mode=block"
# The X-Content-Type-Options header. If null, the header is not set.
contentTypeOptions = "nosniff"
# The X-Permitted-Cross-Domain-Policies header. If null, the header is not set.
permittedCrossDomainPolicies = "master-only"
# The Content-Security-Policy header. If null, the header is not set.
contentSecurityPolicy = "default-src 'self'"
# The Referrer-Policy header. If null, the header is not set.
referrerPolicy = "origin-when-cross-origin, strict-origin-when-cross-origin"
# If true, allow an action to use .withHeaders to replace one or more of the above headers
allowActionSpecificHeaders = false
}
§AllowedHostsFilter
AllowedHostsFilterは、許可されたホストのホワイトリストを追加し、ホワイトリストに一致しないホストを持つすべてのリクエストに400(不正なリクエスト)応答を送信します。
§デフォルトで有効になっている理由
これは開発で使用する重要なフィルターです。DNSリバインディング攻撃は、開発者のPlayインスタンスに対して使用できるためです。localhostで実行されているサーバーがどのように短期的なDNSリバインディングによって攻撃されるかについては、Rails Webconsole DNS Rebindingを参照してください。
§どのような変更が必要ですか?
localhost以外の場所でPlayアプリケーションを実行している場合は、接続元のホスト名/IPを明示的に許可するようにAllowedHostsFilterを構成する必要があります。これは、環境を変更する場合に特に重要です。通常、開発環境ではlocalhostで実行しますが、ステージング環境と本番環境ではリモートで実行するためです。
play.filters.hosts {
# Allow requests to example.com, its subdomains, and localhost:9000.
allowed = [".example.com", "localhost:9000"]
}
§フィルターへの追加
デフォルトリストに追加するには、+=
を使用します。
play.filters.enabled+=MyFilter
play.api.http.DefaultHttpFilters
を拡張して独自のフィルターを定義した場合は、EnabledFilters
を独自のリストとコード内で結合することもできるため、以前に定義したプロジェクトは通常どおりに機能します。
class Filters @Inject()(enabledFilters: EnabledFilters, corsFilter: CORSFilter)
extends DefaultHttpFilters(enabledFilters.filters :+ corsFilter: _*)
§デフォルトフィルターのテスト
複数のフィルターが有効になっているため、すべてのテストが合格し、リクエストが有効であることを確認するために、機能テストをわずかに変更する必要がある場合があります。たとえば、Host
HTTPヘッダーがlocalhost
に設定されていないリクエストは、AllowedHostsFilterに合格せず、代わりに400 Forbidden応答を返します。
§AllowedHostsFilterでのテスト
AllowedHostsFilterフィルターが自動的に追加されるため、機能テストにはHost HTTPヘッダーを追加する必要があります。
FakeRequest
または Helpers.fakeRequest
を使用している場合、Host
HTTPヘッダーは自動的に追加されます。play.mvc.Http.RequestBuilder
を使用している場合は、ヘッダーを手動で追加するための独自の行を追加する必要がある場合があります。
RequestBuilder request = new RequestBuilder()
.method(GET)
.header(HeaderNames.HOST, "localhost")
.uri("/xx/Kiwi");
§CSRFFilterでのテスト
CSRFFilterフィルターが自動的に追加されるため、CSRF.formField
を含むTwirlテンプレートをレンダリングするテスト(例:
@(userForm: Form[UserData])(implicit request: RequestHeader, m: Messages)
<h1>user form</h1>
@request.flash.get("success").getOrElse("")
@helper.form(action = routes.UserController.userPost()) {
@helper.CSRF.formField
@helper.inputText(userForm("name"))
@helper.inputText(userForm("age"))
<input type="submit" value="submit"/>
}
リクエストにCSRFトークンを含める必要があります。Scala APIでは、これは play.api.test.CSRFTokenHelper._
をインポートすることで行われます。これにより、play.api.test.FakeRequest
に withCSRFToken
メソッドが追加されます。
import play.api.test.CSRFTokenHelper._
class UserControllerSpec extends PlaySpec with GuiceOneAppPerTest {
"UserController GET" should {
"render the index page from the application" in {
val controller = app.injector.instanceOf[UserController]
val request = FakeRequest().withCSRFToken
val result = controller.userGet().apply(request)
status(result) mustBe OK
contentType(result) mustBe Some("text/html")
}
}
}
Java APIでは、これはplay.mvc.Http.RequestBuilder
インスタンスで CSRFTokenHelper.addCSRFToken
を呼び出すことによって行われます。
requestBuilder = CSRFTokenHelper.addCSRFToken(requestBuilder);
§デフォルトフィルターの無効化
デフォルトフィルターを無効にする最も簡単な方法は、application.conf
でフィルターのリストを手動で設定することです。
play.filters.enabled=[]
これは、デフォルトのフィルターを通過させたくない機能テストがある場合に役立つ場合があります。
すべてのフィルタークラスを削除したい場合は、disablePlugins
メカニズムを使用して無効にできます。
lazy val root = project.in(file(".")).enablePlugins(PlayScala).disablePlugins(PlayFilters)
または、EnabledFilters
を置き換えることでも可能です。
play.http.filters=play.api.http.NoHttpFilters
GuiceApplicationBuilder
を含む機能テストを作成していて、デフォルトのフィルターを無効にしたい場合は、configure
を使用して設定を通じてすべてのフィルターまたは一部のフィルターを無効にできます。
GuiceApplicationBuilder().configure("play.http.filters" -> "play.api.http.NoHttpFilters")
§コンパイル時デフォルトフィルター
コンパイル時依存性注入を使用している場合、デフォルトのフィルターは実行時ではなくコンパイル時に解決されます。
つまり、BuiltInComponents
トレイトには抽象メソッドである httpFilters
メソッドが含まれるようになりました。
trait BuiltInComponents {
/** A user defined list of filters that is appended to the default filters */
def httpFilters: Seq[EssentialFilter]
}
デフォルトのフィルターリストは play.filters.HttpFiltersComponents
で定義されています。
trait HttpFiltersComponents
extends CSRFComponents
with SecurityHeadersComponents
with AllowedHostsComponents {
def httpFilters: Seq[EssentialFilter] = Seq(csrfFilter, securityHeadersFilter, allowedHostsFilter)
}
ほとんどの場合、HttpFiltersComponents をミックスインし、独自のフィルターを追加することになるでしょう。
class MyComponents(context: ApplicationLoader.Context)
extends BuiltInComponentsFromContext(context)
with play.filters.HttpFiltersComponents {
lazy val loggingFilter = new LoggingFilter()
override def httpFilters = {
super.httpFilters :+ loggingFilter
}
}
リストから要素をフィルタリングしたい場合は、次のようにできます。
class MyComponents(context: ApplicationLoader.Context)
extends BuiltInComponentsFromContext(context)
with play.filters.HttpFiltersComponents {
override def httpFilters = {
super.httpFilters.filterNot(_.getClass == classOf[CSRFFilter])
}
}
§コンパイル時デフォルトフィルターの無効化
デフォルトのフィルターを無効にするには、play.api.NoHttpFiltersComponents
をミックスインします。
class MyComponents(context: ApplicationLoader.Context)
extends BuiltInComponentsFromContext(context)
with NoHttpFiltersComponents
with AssetsComponents {
lazy val homeController = new HomeController(controllerComponents)
lazy val router = new Routes(httpErrorHandler, homeController, assets)
}
§JWTサポート
Playのクッキーエンコーディングは、内部でJSON Web Token(JWT)を使用するように切り替えられました。JWTには、HMAC-SHA-256による自動署名や、セッションクッキーが特定の時間枠外で再利用されないようにするための自動的な「有効期限前」と「有効期限後」の日付チェックのサポートなど、多くの利点があります。
詳細については、セッションクッキーの設定ページを参照してください。
§フォールバッククッキーサポート
Playのクッキーエンコーディングは、「フォールバック」クッキーエンコーディングメカニズムを使用しており、JWTエンコードされたクッキーを読み込み、JWT解析が失敗した場合はURLエンコードされたクッキーを読み込もうとするため、既存のセッションクッキーをJWTに安全に移行できます。この機能は、FallbackCookieDataCodec
トレイトにあり、DefaultSessionCookieBaker
および DefaultFlashCookieBaker
で活用されています。
§レガシーサポート
JWTエンコードされたクッキーの使用はシームレスであるはずですが、必要に応じて、application.confファイルで play.api.mvc.LegacyCookiesModule
に切り替えることで、URLエンコードされたクッキーエンコーディングに戻すことができます。
play.modules.disabled+="play.api.mvc.CookiesModule"
play.modules.enabled+="play.api.mvc.LegacyCookiesModule"
§カスタム CookieBakers
Playで使用されているカスタムクッキー(CookieBaker[T]
トレイトを使用)がある場合は、カスタムクッキーベイカーにどのようなエンコーディングを使用するかを指定する必要があります。
ブラウザで見つかった形式との間で Map[String, String]
をエンコードおよびデコードするメソッドは、CookieDataCodec
に抽出されています。3つの実装があります。FallbackCookieDataCodec
、JWTCookieDataCodec
、または UrlEncodedCookieDataCodec
であり、それぞれHMACによるURLエンコード、JWT、または「署名済みまたはJWTを読み込み、JWTを書き込む」コーデックを表します。
また、JWTConfigurationParser
を使用して設定へのパスを指定した JWTConfiguration
ケースクラスを提供するか、デフォルトの場合は JWTConfiguration()
を使用する必要があります。
@Singleton
class UserInfoCookieBaker @Inject()(service: UserInfoService,
val secretConfiguration: SecretConfiguration)
extends CookieBaker[UserInfo] with JWTCookieDataCodec {
override val COOKIE_NAME: String = "userInfo"
override val isSigned = true
override def emptyCookie: UserInfo = new UserInfo()
override protected def serialize(userInfo: UserInfo): Map[String, String] = service.encrypt(userInfo)
override protected def deserialize(data: Map[String, String]): UserInfo = service.decrypt(data)
override val path: String = "/"
override val jwtConfiguration: JWTConfiguration = JWTConfigurationParser()
}
§非推奨の Futures メソッド
次の play.libs.concurrent.Futures
静的メソッドは非推奨となりました。
timeout(A value, long amount, TimeUnit unit)
timeout(final long delay, final TimeUnit unit)
delayed(Supplier<A> supplier, long delay, TimeUnit unit, Executor executor)
代わりに、依存性注入された Futures
のインスタンスを使用する必要があります。
class MyClass {
@Inject
public MyClass(play.libs.concurrent.Futures futures) {
this.futures = futures;
}
CompletionStage<Double> callWithOneSecondTimeout() {
return futures.timeout(computePIAsynchronously(), Duration.ofSeconds(1));
}
}
§更新されたライブラリ
§Netty 4.1
Nettyはバージョン4.1にアップグレードされました。これは主に、バージョン4.0がplay-wsのスタンドアロンモジュールへの移行によってシェーディングされたためです。したがって、Netty ServerとNetty 4.0に依存するライブラリを使用している場合は、ライブラリの新しいバージョンへのアップグレードを試すか、Akka Serverの使用を開始することをお勧めします。
また、何らかの理由でNettyクラスを直接使用している場合は、この新しいバージョンに合わせてコードを調整する必要があります。
§FluentLeniumとSelenium
FluentLeniumライブラリがバージョン3.2.0に更新され、Seleniumが3.3.1に更新されました(changelogはこちらを参照してください)。以前にSeleniumのWebDriver APIを使用していた場合は、何もする必要はありません。こちらの発表で詳細を確認してください。
FluentLeniumライブラリを使用していた場合は、テストを再度機能させるために、いくつかの構文を変更する必要がある場合があります。コードを適応させる方法の詳細については、FluentLeniumの移行ガイドを参照してください。
§HikariCP
HikariCPが更新され、新しい構成 initializationFailTimeout
が導入されました。この新しい構成は、現在非推奨となっている initializationFailFast
を置き換えるために使用する必要があります。HikariCP changelog および initializationFailTimeout
のドキュメントを参照して、この新しい構成の使用方法をより深く理解してください。
§その他の構成変更
いくつかの構成変更があります。古い構成パスは通常、まだ機能しますが、それらを使用すると、実行時に非推奨の警告が出力されます。変更されたキーの概要を以下に示します。
古いキー | 新しいキー |
---|---|
play.crypto.secret |
play.http.secret.key |
play.crypto.provider |
play.http.secret.provider |
play.websocket.buffer.limit |
play.server.websocket.frame.maxLength |
次へ: メッセージの移行
このドキュメントにエラーを見つけましたか?このページのソースコードはこちらにあります。ドキュメントガイドラインを読んだ後、お気軽にプルリクエストを送信してください。質問やアドバイスがありますか?コミュニティフォーラムに参加して、コミュニティとの会話を始めましょう。