Documentation

You are viewing the documentation for the 2.1.5 release in the 2.1.x series of releases. The latest stable release series is 2.4.x.

§JSON リクエストとレスポンス

§JSON リクエストの処理

JSON リクエストは JSON データをリクエストボディに含む HTTP リクエストです。JSON リクエストは、Content-Type ヘッダに text/jsonapplication/json という MIME タイプを指定する必要があります。

Actionany content ボディパーサーをデフォルトで使います。これを利用して、リクエストボディを JSON (具体的には、JsValue) として取得することができます。

package controllers

import play.api._
import play.api.mvc._
import play.api.libs.json._
// you need this import to have combinators
import play.api.libs.functional.syntax._

object Application extends Controller {
  
  implicit val rds = (
    (__ \ 'name).read[String] and
    (__ \ 'age).read[Long]
  ) tupled

  def sayHello = Action { request =>
    request.body.asJson.map { json =>
      json.validate[(String, Long)].map{ 
        case (name, age) => Ok("Hello " + name + ", you're "+age)
      }.recoverTotal{
        e => BadRequest("Detected error:"+ JsError.toFlatJson(e))
      }
    }.getOrElse {
      BadRequest("Expecting Json data")
    }
  }
}

この場合、専用の BodyParser を指定することで Play にコンテントボディを直接的に JSON としてパースさせると、記述がシンプル化されてなお良いでしょう。

  def sayHello = Action(parse.json) { request =>
    request.body.validate[(String, Long)].map{ 
      case (name, age) => Ok("Hello " + name + ", you're "+age)
    }.recoverTotal{
      e => BadRequest("Detected error:"+ JsError.toFlatJson(e))
    }
  }

Note: JSON ボディパーサーを利用すると、request.body の値が直接 JsValue として扱えるようになります。

注意:

§implicits Reads[(String, Long)]

入力された JSON のバリデーションと変換を行うことのできるコンビネータを使う、暗黙の Reads を定義します。

§json.validate[(String, Long)]

暗黙の Reads[(String, Long)] に従って、入力された JSON のバリデーションと変換を明示的に行います。

このアクションは、コマンドラインから cURL を使って以下のようにテストできます。

§json.validate[(String, Long)].map{ (String, Long) => ... }

JSON の変換に成功した場合、その結果をアクションの結果にマップします。

§json.validate[(String, Long)].recoverTotal{ e: JsError => ... }

recoverTotal は、発生したエラーに対応して、デフォルト値を返す関数を受け取ります:
- JsResult の変更チェーンを終了し、正常に作成された内部文字列を返します
- または、失敗を検出した場合は recoverToal に渡された関数の実行結果を返します。

§JsError.toFlatJson(e)

JsError を平べったい JsObject 形式に変換するヘルパーです :

JsError(List((/age,List(ValidationError(validate.error.missing-path,WrappedArray()))), (/name,List(ValidationError(validate.error.missing-path,WrappedArray())))))

これは、次のような JsValue になります:

{"obj.age":[{"msg":"validate.error.missing-path","args":[]}],"obj.name":[{"msg":"validate.error.missing-path","args":[]}]}

今後、この他にもいくつかのヘルパーが提供される予定です。

試してみましょう

§成功した場合

curl 
  --header "Content-type: application/json" 
  --request POST 
  --data '{"name": "Toto", "age": 32}' 
  http://localhost:9000/sayHello

レスポンスは以下のようになります。

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 47

Hello Toto, you're 32

§失敗した場合 “見つからない JSON フィールド”

curl 
  --header "Content-type: application/json" 
  --request POST 
  --data '{"name2": "Toto", "age2": 32}' 
  http://localhost:9000/sayHello

レスポンスは以下のようになります。

HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Content-Length: 106

Detected error:{"obj.age":[{"msg":"validate.error.missing-path","args":[]}],"obj.name":[{"msg":"validate.error.missing-path","args":[]}]}

§失敗した場合 “不正な JSON 型”

curl 
  --header "Content-type: application/json" 
  --request POST 
  --data '{"name": "Toto", "age": "chboing"}' 
  http://localhost:9000/sayHello

レスポンスは以下のようになります。

HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Content-Length: 100

Detected error:{"obj.age":[{"msg":"validate.error.expected.jsnumber","args":[]}]}

§JSON レスポンスの送信

前述の例ではリクエストを JSON で受けていましたが、レスポンスは text/plain として送信していました。これを、正しい JSON HTTP レスポンスを送り返すように変更してみましょう。

  def sayHello = Action(parse.json) { request =>
    request.body.validate[(String, Long)].map{ 
      case (name, age) => Ok(Json.obj("status" ->"OK", "message" -> ("Hello "+name+" , you're "+age) ))
    }.recoverTotal{
      e => BadRequest(Json.obj("status" ->"KO", "message" -> JsError.toFlatJson(e)))
    }
  }

レスポンスは以下のようになります。

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 47

{"status":"OK","message":"Hello Toto, you're 32"}

§JSON を直接返す

Play 2.1 と JSON で Todo リストを返すのはとても簡単です:

import play.api.libs.json.Json

def tasksAsJson() = Action {
  Ok(Json.toJson(Task.all().map { t=>
    (t.id.toString, t.label)
  } toMap))
}

次ページ: XML