§Play 2.7 の新機能
このページでは、Play 2.7 の新機能について説明します。Play 2.7 へのマイグレーションに必要な変更点については、Play 2.7 マイグレーションガイドを参照してください。
§Scala 2.13 のサポート
Play 2.7 は、Scala 2.13、2.12、および 2.11 に対してクロスビルドされる最初の Play リリースです。これを達成するために、多くの依存関係が更新されました。
build.sbt
でscalaVersion
設定することで、使用する Scala のバージョンを選択できます。
Scala 2.12 の場合
scalaVersion := "2.12.19"
Scala 2.11 の場合
scalaVersion := "2.11.12"
Scala 2.13 の場合
scalaVersion := "2.13.13"
§Akka の Coordinated Shutdown によるライフサイクル管理
Play 2.6 では、Akka のCoordinated Shutdownの使用が導入されましたが、コアフレームワーク全体で使用されていなかったり、エンドユーザーに公開されていませんでした。Coordinated Shutdown は、アクタシステムのシャットダウン中に順序付けられた方法で実行できるタスクのレジストリを持つ Akka 拡張機能です。
Coordinated Shutdown は内部的に Play 2.7 のライフサイクルを処理し、CoordinatedShutdown
のインスタンスをインジェクションで利用できます。Coordinated Shutdown は、Play のアプリケーションライフサイクルのような単一フェーズを持つのではなく、有向非巡回グラフ (DAG)として整理された、より細かい粒度のフェーズを提供します。たとえば、サーバーバインディングの前後、または現在リクエストがすべて完了した後に実行するタスクを追加できます。Akka Clusterとの統合も向上します。
Play マニュアルのCoordinated Shutdown に関する新しいセクションで詳細を確認するか、Akka のCoordinated Shutdown に関するリファレンスドキュメントを参照してください。
§Guice が 4.2.2 にアップグレードされました
Play で使用されるデフォルトの依存性注入フレームワークである Guice が 4.1.0 から 4.2.2 にアップグレードされました。4.2.2、4.2.1、および4.2.0のリリースノートを参照してください。この新しい Guice バージョンには破壊的変更が導入されているため、Play 2.7 マイグレーションガイドを確認してください。
§Java フォームによる multipart/form-data
ファイルアップロードのバインド
Play 2.6 までは、multipart/form-data
エンコードされたフォームを介してアップロードされたファイルを取得する唯一の方法は、アクションメソッド内で呼び出すrequest.body().asMultipartFormData().getFile(...)
ことでした。
Play 2.7 以降、このようなアップロードされたファイルは Java フォームにもバインドされるようになりました。カスタムの multipart ファイルパートボディパーサーを使用していない場合は、フォームにTemporaryFile
型のFilePart
を追加するだけで済みます。
import play.libs.Files.TemporaryFile;
import play.mvc.Http.MultipartFormData.FilePart;
public class MyForm {
private FilePart<TemporaryFile> myFile;
public void setMyFile(final FilePart<TemporaryFile> myFile) {
this.myFile = myFile;
}
public FilePart<TemporaryFile> getMyFile() {
return this.myFile;
}
}
以前と同様に、コントローラーにインジェクションされたFormFactory
を使用してフォームを作成します。
Form<MyForm> form = formFactory.form(MyForm.class).bindFromRequest(req);
バインディングが成功した場合(フォームの検証がパスした場合)、ファイルにアクセスできます。
MyForm myform = form.get();
myform.getMyFile();
アップロードされたファイルの処理に役立つメソッドも追加されました。
// Get all files of the form
form.files();
// Access the file of a Field instance
Field myFile = form.field("myFile");
field.file();
// To access a file of a DynamicForm instance
dynamicForm.file("myFile");
注記:カスタムの multipart ファイルパートボディパーサーを使用している場合は、
TemporaryFile
をボディパーサーで使用している型に置き換えるだけです。
§Play Java で提供される制約アノテーションは @Repeatable になりました
play.data.validation.Constraints
によって定義されているすべての制約アノテーションは、現在@Repeatable
です。この変更により、たとえば、同じ要素で同じアノテーションを複数回使用できますが、毎回異なるgroups
を使用できます。ただし、一部の制約については、@ValidateWith
のように、それ自体を繰り返す方が理にかなっています。
@Validate(groups={GroupA.class})
@Validate(groups={GroupB.class})
public class MyForm {
@ValidateWith(MyValidator.class)
@ValidateWith(MyOtherValidator.class)
@Pattern(value="[a-k]", message="Should be a - k")
@Pattern(value="[c-v]", message="Should be c - v")
@MinLength(value=4, groups={GroupA.class})
@MinLength(value=7, groups={GroupB.class})
private String name;
//...
}
もちろん、独自のカスタム制約を@Repeatable
にすることもでき、Play は自動的にそれを認識します。
§Java の validate
メソッドと isValid
メソッドのペイロード
高度な検証機能を使用する場合、検証プロセスに必要な情報を時々含むValidationPayload
オブジェクトを、Java のvalidate
メソッドまたはisValid
メソッドに渡すことができます。
このようなペイロードをvalidate
メソッドに渡すには、フォームに@ValidateWithPayload
(@Validate
の代わりに)アノテーションを付け、ValidatableWithPayload
(Validatable
の代わりに)を実装します。
import java.util.Map;
import com.typesafe.config.Config;
import play.data.validation.Constraints.ValidatableWithPayload;
import play.data.validation.Constraints.ValidateWithPayload;
import play.data.validation.Constraints.ValidationPayload;
import play.i18n.Lang;
import play.i18n.Messages;
import play.libs.typedmap.TypedMap;
@ValidateWithPayload
public class SomeForm implements ValidatableWithPayload<String> {
@Override
public String validate(ValidationPayload payload) {
Lang lang = payload.getLang();
Messages messages = payload.getMessages();
Map<String, Object> ctxArgs = payload.getArgs();
TypedMap attrs = payload.getAttrs();
Config config = payload.getConfig();
// ...
}
}
独自のカスタムクラスレベル制約を作成した場合は、PlayConstraintValidatorWithPayload
(PlayConstraintValidator
の代わりに)を実装することで、isValid
メソッドにペイロードを渡すこともできます。
import javax.validation.ConstraintValidatorContext;
import play.data.validation.Constraints.PlayConstraintValidatorWithPayload;
import play.data.validation.Constraints.ValidationPayload;
// ...
public class ValidateWithDBValidator implements PlayConstraintValidatorWithPayload<SomeValidatorAnnotation, SomeValidatableInterface<?>> {
//...
@Override
public boolean isValid(final SomeValidatableInterface<?> value, final ValidationPayload payload, final ConstraintValidatorContext constraintValidatorContext) {
// You can now pass the payload on to your custom validate(...) method:
return reportValidationStatus(value.validate(...., payload), constraintValidatorContext);
}
}
注記:
ValidationPayload
とConstraintValidatorContext
を混同しないでください。前者は Play によって提供されるクラスであり、Play でフォームを扱う際の日常業務で使用します。後者はBean Validation 仕様によって定義されたクラスであり、Play の内部でのみ使用されます。ただし、上記の最後の例のように、独自のカスタムクラスレベル制約を作成する場合を除きます。この場合、reportValidationStatus
メソッドに渡すだけで済みます。
§Caffeine のサポート
Play は、Caffeineに基づいた CacheApi 実装を提供するようになりました。Caffeine は Play ユーザー向けの推奨されるキャッシュ実装です。
EhCache から Caffeine にマイグレーションするには、依存関係からehcache
を削除してcaffeine
に置き換える必要があります。デフォルトの設定をカスタマイズするには、ドキュメントの説明に従って、application.conf の設定も更新する必要があります。
Java キャッシュ APIとScala キャッシュ APIのドキュメントを参照して、Play を使用したキャッシュの設定の詳細を確認してください。
§新しいコンテンツセキュリティポリシーフィルター
埋め込まれたコンテンツに対して CSP nonce とハッシュをサポートする新しいコンテンツセキュリティポリシーフィルターが利用可能になりました。
CSP をデフォルトで有効にし、default-src 'self'
に設定する以前の設定は厳しすぎ、プラグインと干渉していました。CSP フィルターはデフォルトで有効になっておらず、SecurityHeaders フィルターのcontentSecurityPolicy
は非推奨になり、デフォルトでnull
に設定されています。
CSP フィルターは、デフォルトで Google のStrict CSP ポリシー(nonce ベースのポリシー)を使用します。これは出発点として推奨され、含まれている CSPReport ボディパーサーとアクションを使用して、本番環境で CSP を適用する前に CSP 違反をログに記録します。
§HikariCP のアップグレード
HikariCPが最新のメジャーバージョンに更新されました。変更点については、マイグレーションガイドを参照してください。
§Java 用 Play WS curl
フィルター
Play WS を使用すると、行われたリクエストを検査または強化するためのplay.libs.ws.WSRequestFilter
を作成できます。Play は「curl としてログ出力する」フィルターを提供していますが、これは Java 開発者には不足していました。次のような記述が可能になりました。
ws.url("https://play.dokyumento.jp")
.setRequestFilter(new AhcCurlRequestLogger())
.addHeader("My-Header", "Header value")
.get();
次に、次のログが出力されます。
curl \
--verbose \
--request GET \
--header 'My-Header: Header Value' \
'https://play.dokyumento.jp'
これは、リクエストを独立して再現し、curl
パラメーターを変更して動作を確認する場合に特に役立ちます。
§Gzip フィルターが圧縮レベルの設定をサポートするようになりました
gzip エンコーディングを使用する場合、使用する圧縮レベルを設定できるようになりました。たとえば、play.filters.gzip.compressionLevel
を使用して設定できます。
play.filters.gzip.compressionLevel = 9
GzipEncodingで詳細を確認してください。
§API の追加
Play 2.7.0 で行った関連する API の追加の一部を次に示します。
§Result HttpEntity
ストリームメソッド
以前のバージョンの Play には、HTTP チャンク転送エンコーディングを使用して結果をストリームするための便利なメソッドがありました。
- Java
-
public Result chunked() { Source<ByteString, NotUsed> body = Source.from(Arrays.asList(ByteString.fromString("first"), ByteString.fromString("second"))); return ok().chunked(body); }
- Scala
-
def chunked = Action { val body = Source(List("first", "second", "...")) Ok.chunked(body) }
Play 2.6 では、HTTP チャンクエンコーディングを使用せずに同じようにストリームされた Result を返す便利なメソッドがありませんでした。代わりに次のように記述する必要がありました。
- Java
-
public Result streamed() { Source<ByteString, NotUsed> body = Source.from(Arrays.asList(ByteString.fromString("first"), ByteString.fromString("second"))); return ok().sendEntity(new HttpEntity.Streamed(body, Optional.empty(), Optional.empty())); }
- Scala
-
def streamed = Action { val body = Source(List("first", "second", "...")).map(s => ByteString.fromString(s)) Ok.sendEntity(HttpEntity.Streamed(body, None, None)) }
Play 2.7 では、chunked
と同様に動作する新しいstreamed
メソッドが結果に追加されることで、この問題が解決されました。
- Java
-
public Result streamed() { Source<ByteString, NotUsed> body = Source.from(Arrays.asList(ByteString.fromString("first"), ByteString.fromString("second"))); return ok().streamed(body, Optional.empty(), Optional.empty()); }
- Scala
-
def streamed = Action { val body = Source(List("first", "second", "...")).map(s => ByteString.fromString(s)) Ok.streamed(body, contentLength = None) }
§新しい HTTP エラーハンドラー
Play 2.7 は、play.api.http.HttpErrorHandler
に 2 つの新しい実装をもたらします。1 つ目はJsonHttpErrorHandler
で、JSON 形式でフォーマットされたエラーを返し、JSON ペイロードを受け入れて返す REST API を開発する場合の優れた代替手段です。2 つ目はHtmlOrJsonHttpErrorHandler
で、クライアントのAccept
ヘッダーで指定された設定に基づいて HTML または JSON エラーを返します。これは、現代の Web アプリで一般的であるように、アプリケーションが HTML と JSON の両方を使用する場合に最適なオプションです。
詳細は、JavaまたはScalaのドキュメントをご覧ください。
§Router.withPrefix
のより簡潔な構文
Play 2.7では、play.api.routing.Router.withPrefix
を使用するためのシンタックスシュガーを導入しました。従来の書き方
val router = apiRouter.withPrefix("/api")
は、以下のように書くことができます。
val router = "/api" /: apiRouter
さらに、複数のパスセグメントを組み合わせることも可能です。
val router = "/api" /: "v1" /: apiRouter
§ルーターの連結
Play 2.7では、Router
をプログラム的に合成するための新しいメソッドorElse
を導入しました。
ルーターは以下のように合成できます。
- Java
-
Router router = oneRouter.orElse(anotherRouter)
- Scala
-
val router = oneRouter.orElse(anotherRouter)
§データベーストランザクションの分離レベル
play.api.db.Database.withTransaction
API(Javaユーザーの場合はplay.db.Database
)を使用する際に、分離レベルを選択できるようになりました。例えば
- Java
-
public void someDatabaseOperation() { database.withTransaction(TransactionIsolationLevel.ReadUncommitted, connection -> { ResultSet resultSet = connection.prepareStatement("select * from users where id = 10").executeQuery(); // consume the resultSet and return some value }); }
- Scala
-
def someDatabaseOperation(): Unit = { database.withTransaction(TransactionIsolationLevel.ReadUncommitted) { connection => val resultSet: ResultSet = connection.prepareStatement("select * from users where id = 10").executeQuery(); // consume the resultSet and return some value } }
利用可能なトランザクション分離レベルは、java.sql.Connection
で定義されているものと同様です。
次へ: 移行ガイド
このドキュメントに誤りを見つけましたか?このページのソースコードはこちらにあります。ドキュメントガイドラインをお読みになった後、プルリクエストを送信してご協力ください。ご質問やアドバイスがありましたら、コミュニティフォーラムでコミュニティと話し合ってください。