ScalaでPlayFramework: コントローラ編

この記事は以下の記事の続きです。
[ScalaでPlayFramework: 環境構築編](http://qiita.com/Yaruki00/items/f9770edea44c1fed6f78)

Playは公式ドキュメントがちゃんとあるので、細かいところはそちらを参照してください。
[Play 2.5.10 documentation](https://www.playframework.com/documentation/2.5.10/Home)
この記事を執筆している段階では、日本語化されているのは2.4までみたいなので、頑張って英語読みましょう。
全てが全て変わっているというわけでもないので、基本2.4を見つつ、うまく行かなかったら2.5見てみるでもいいかもしれません。
この記事の内容は、だいたい以下のページに書かれています。

* [Actions, Controllers and Results](https://www.playframework.com/documentation/2.5.x/ScalaActions)
* [HTTP routing](https://www.playframework.com/documentation/2.5.x/ScalaRouting)
* [Manipulating Results](https://www.playframework.com/documentation/2.5.x/ScalaResults)
* [Session and Flash scopes](https://www.playframework.com/documentation/2.5.x/ScalaSessionFlash)

# ルーティングを見てみる
プロジェクトルート以下の`conf/routes`を開きます。初期状態では以下のようになっているはずです。

```scala:conf/routes
...
# An example controller showing a sample home page
GET     /                           controllers.HomeController.index
# An example controller showing how to use dependency injection
GET     /count                      controllers.CountController.count
# An example controller showing how to write asynchronous code
GET     /message                    controllers.AsyncController.message
...
```

`method /path action`と書くことで、`/path`に`method`でアクセスが来たとき`action`が実行されるようになります。
下でコントローラにアクションを書いていきますが、アクションができたら都度このファイルに追加していきます。

# コントローラをいじる
プロジェクトルート以下の`app/controller/HomeController.scala`を開きます。最初はアクションが一つだけあります。

```scala:app/controller/HomeController.scala
...
@Singleton
class HomeController @Inject() extends Controller {
...
  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }
}
```

アクションを作るには、ズバリ`Action`を定義してやればいいです。

## 文字列を返す
とりあえずHello Worldしてみましょう。
`app/controller/HomeController.scala`に以下を追加します。

```scala:app/controller/HomeController.scala
...
  def hello = Action {
    Ok("World")
  }
...
```

ルーティングにも追加します。

```scala:conf/routes
...
GET     /hello                      controllers.HomeController.hello
...
```

できたら、プロジェクトを起動してブラウザからアクセスしてみましょう。
http://localhost:9000/hello
`World`という文字列が表示されればOKです。

----

ちなみに、`run`でプロジェクトを起動すれば、ファイルを更新した時にいちいちプロジェクトを再起動する必要はありません。
ファイルに更新があった場合は、アクセス時にコンパイルが走ります。その為、更新した直後1回目のアクセスは少し時間がかかります。

もし、ファイルを更新後アクセスすると反応がなくなってしまう場合は、以下の設定を追加するとうまくいくようになるかもしれません。

```scala:build.sbt
...
fork in run := false
```

----

## パラメータを受け取る
アクションに引数をつけることでパラメータを受け取れるようになります。

```scala:app/controller/HomeController.scala
...
  def show(id: String) = Action {
    Ok(id)
  }
...
```

### URLのパスの一部を受け取る
`/`から次の`/`までのパスを受け取る場合は`:id`を使います(idは任意の文字列)。
パスは特に指定しない限り、`String`で受け取ることになります。

```scala:conf/routes
...
GET     /show/:id/go                 controllers.HomeController.show(id)
...
```
例えば、`/show/1/go`とか`/show/hello/go`とかがマッチします。その時`id`となるのは、`1`と`hello`です。

ある`/`以降の全てのパスを受け取る場合は`*path`を使います(pathは任意の文字列)。

```scala:conf/routes
...
GET     /show/*path                 controllers.HomeController.show(path)
...
```

`/show/hoge/fuga`の場合、`path`は`hoge/fuga`になります。

正規表現を使いたい場合は、`$id<regex>`を使います(idは任意の文字列、regexは正規表現)。

```scala:conf/routes
...
GET     /regex/$id<[0-9]+>          controllers.HomeController.show(id)
...
```

`/regex/1`とか`/regex/9999`とかがマッチします。

----

ちなみに、`conf/routes`は上から順番にマッチするかの判定が行われます。
例えば下記のように書いてしまうと、`controllers.SomeController.hello`は絶対に呼ばれません。注意しましょう。

```scala:conf/routes
GET     /show/:id                   controllers.SomeController.show(id)
GET     /show/hello                 controllers.SomeController.hello
```

逆にこれを利用し、一番最後にどんなパスにもマッチする様書いておけば、自作の404ページを表示させたりすることもできます。

```scala:conf/routes
...
# 最後
GET     /*any                       controllers.SomeController.response404(any)
```

----

### GET(POST)パラメータを受け取る
例えば以下のようなアクションを定義します。

```scala:app/controller/HomeController.scala
...
  def params(a: String, b: Int, c: Option[String]) = Action {
    Ok("%s, %d, %s".format(a, b, c))
  }
...
```

ルーティングには以下のように書きます。

```scala:conf/routes
...
GET     /params                     controllers.HomeController.params(a, b: Int, c: Option[String])
...
```

これだけです。`/params?a=hoge&b=1&c=fuga`みたいな感じでアクセスすれば、パラメータが渡せていることが確認できます。
パラメータで`String`以外のものを扱う場合は、型を明記する必要があります。
型を`Option`にすることで、あっても無くてもいいパラメータを作ることができます。

パラメータにデフォルト値をもたせたい場合は以下のようにします。

```scala:conf/routes
...
GET     /params_b_default           controllers.HomeController.params(a, b: Int = 1, c: Option[String])
...
```

`/params_b_default?a=hoge`みたいな感じでアクセスできます。もちろん`b`や`c`に値を指定しても構いません。

## リダイレクトする
一行でできます。

```scala:app/controller/HomeController.scala
...
  def redirectHello = Action {
    Redirect("/hello")
  }
...
```

## レスポンスヘッダをいじる、セッションを使う、JSONで返す
3つまとめて。後ろにくっつけます。

```scala:app/controller/HomeController.scala
...
  def additional = Action {
    Ok("additional")
      .withHeaders(
          CACHE_CONTROL -> "max-age=3600",
          ETAG -> "xx"
      )
      .withSession(
          "connected" -> "hoge",
          "foo" -> "bar"
      )
      .as(JSON)
  }
...
```

セッションのチェックは以下の感じで。`connected`はただのキーなので、お好きな名前で大丈夫です。

```scala:app/controller/HomeController.scala
...
  def checkSession = Action { request =>
    request.session.get("connected").map { user =>
      Ok("Hello " + user)
    }.getOrElse {
      Unauthorized("you are not connected")
    }
  }
...
```

セッションをクリアするにはこうします。

```scala:app/controller/HomeController.scala
...
  def clearSession = Action { request =>
    Ok("cleared")
      .withNewSession
  }
...
```

## Flashとやらを使う
Flashは1度の遷移のみ有効なセッション、みたいなものらしいです。

```scala:app/controller/HomeController.scala
  def redirectOnly = Action { implicit request =>
    Ok {
      request.flash.get("success").getOrElse("Welcome!")
    }
  }

  def success = Action {
    Redirect("/redirect_only")
      .flashing(
          "success" -> "yeah!!"
      )
  }
```

# おわりに
公式ドキュメントがよくできています。~~もうそっち読めばいいんじゃないかな。~~
自分なりにまとめるっていうのも必要ですよね。うん、きっとそうに違いない。
次はビューを扱います。

次: [ScalaでPlayFramework: ビュー編](http://qiita.com/Yaruki00/items/1ff93cd2df9a008c2413)