Play Framework
世の中には多様なウェブシステムのためのフレームワークがありますが、今回はPlayに注目して話します。Javaには伝統的にJavaEEというものがあり、最近はやりのkotlinでのコーディングが可能なSpringもかなり使いやすくなっていますが、scalaなどの親和性も考慮するとPlayが一番よさそうなため。アーキテクチャもモダンなので管理がしやすい印象です。
Playのインストール
以前はactivatorというツールがあったようですが、今は提供されていないようです。公式ドキュメントによると現在はGitHubからスターターコードをダウンロードするか、sbtのgilter8テンプレートを使うというのが推奨されている始め方のようです。gilter8を使う場合は以下のようにします。
sbt new playframework/play-scala-seed.g8
該当フォルダに移動して、sbt runとするとhttp://localhost:9000にサンプルアプリが立ち上がります。まずはこれで準備は完了です。ちなみにこの状態でも、scalaに特有のauto-reload機能が有効なので、ソースコードを修正すると即座に再コンパイルされ、再スタートされます。便利ですね。ちなみにこれはtrigger executionという機能で、~をsbtコマンド前にいれると、どのコマンドでも実行できます。(~ compile, ~ run, ~ test, etc...)
IDEのセットアップ
上記のauto-reload機能があれば、特にIDEはいらないともいえますが、生産性を高めるために、EclipseやIntellij IDEAとの連携も考慮されています。ここではIntellij IDEAを使ってセットアップします。事前にIntellijのScala Pluginのインストールをしておきます。
sbtプロジェクトはIntellij IDEAで直接インポートすることができますので、先ほど作ったプロジェクトを読み込みます。新たなRun Configurationを作って、SBT Taskのrunを実行させるようにすれば、上記と同じ環境を再現できます。
Intellijで便利なのは、play.editorコンフィグレーションを使うことで、エラーページからのソースコードへのダイレクトリンクを実現できることです。IntellijではリモートファイルREST APIのための組み込みウェブサーバがポート63342で動いています。これに繋ぐ設定を、conf/application.confに記載します。
play.editor="http://localhost:63342/api/file/?file=%s&line=%s"
加えて、build.sbtに以下の記述を追記します。
fork := true // required for "sbt run" to pick up javaOptions
javaOptions += "-Dplay.editor=http://localhost:63342/api/file/?file=%s&line=%s"
この他にもPLAY_EDITOR環境変数を定義することで実現することもできます。
PLAY_EDITOR="http://localhost:63342/api/file/?file=%s&line=%s"
ルーティングことはじめ
サンプルアプリケーションを起動すると、Playのチュートリアルが確認できます。
PlayはMVCのフレームワークに沿って構成されています。まず最初に理解すべきは、コントローラにウェブのリクエストをルーティングするルールの記載方法です。conf/routesという静的なファイルにルールを記載します。これはアプリケーションで記載するよりもわかりやすいと感じます。例えば以下のように記載します。
GET / controllers.HomeController.index
GET /clients/:id controllers.Clients.show(id: Long)
GET /files/*name controllers.Application.download(name)
1番目の設定はhttp://localhost:9000直下のリクエストを、HomeControllerに定義されている関数であるindexにルーティングする設定です。2番目の設定は、Clientsに定義されている関数であるshowにルーティングするとともに、関数の引数にidを割り当てます。最後の設定はワイルドカードを使ったパターンで、立てばファイル名などimages/log.pngなどにもマッチします。他にも正規表現を使った柔軟なマッチングが可能です。この辺は公式ドキュメントが詳しいです。
コントローラ
ルーティングで定義されたコントローラはControllerを拡張したクラスになります。app/controllers以下に設置します。ControllerはAbstractControllerを拡張したクラスで、前の@Injectアノテーションも必須です。Play2.6以降に仕様が変わったようです。Action型を返す関数を定義する必要がありますが、testの中のOkという関数は実際にはplay.api.mvc.Resultを返します。Okはシンタックスシュガーなので、Resultをnewして返り値とすることも可能です。
package controllers
import play.api._
import play.api.mvc._
class TestContoller @Inject() (cc: ControllerComponents) extends AbstractController(cc) {
def test(String name) = Action {
Ok("Hello " + name + "!");
}
}
同じく、conf.routesに以下を追記します。localhost:9000/test/bobなどとすると、ブラウザにHello bob!と表示されます。
GET /test/:name controllers.TestContoller.test(name:String)
Viewの定義
PlayではTwirlというASP.NETのRazorにインスパイアされたテンプレートエンジンを使うのが一般的のようです。ファイルはapp/views以下に配置されます。@マークを使って、ダイナミックに値を埋め込む記法をとります。公式ドキュメントはこちら。
先ほどのTestControllerを少し変えて、テンプレートエンジンを使うようにしてみます。
package controllers
import play.api._
import play.api.mvc._
class TestContoller @Inject() (cc: ControllerComponents) extends AbstractController(cc) {
def test(String name) = Action {
Ok(views.html.test("Hello " + name + "!");
}
}
app以下にtest.scala.htmlというファイルを用意します。ここに上記から呼び出されるViewsを定義しています。結果は先ほどとまったく同じですが、複数の引数を取って、scalaの構文でlist要素を列挙するといったことは簡単にできます。
@(message: String)
<!DOCTYPE html>
<html lang="ja">
<head>
<title>Test</title>
</head>
<body>
@message
</body>
</html>
おわりに
SpringやJavaEEなどに比べるとPlayが簡易で柔軟なフレームワークであることは、よく理解できます。まだまだ枯れておらず、仕様が細かいところで結構変わっているようなので、最新の公式ドキュメントを追わないといけないのが、ハードルが高くなっている気もしますが、一度覚えてしまえばよっぽど容易かなと感じる次第。次はLagomにも挑戦してみます。