§JSON リクエストとレスポンス
§JSON リクエストの処理
JSON リクエストは JSON データをリクエストボディに含む HTTP リクエストです。JSON リクエストは、Content-Type
ヘッダに text/json
か application/json
という MIME タイプを指定する必要があります。
Action
は any 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