In routes:
POST /login controllers.ApplicationCtrl.login()
In Controller:
def login = Action(parse.json) { implicit request => {
val email = (request.body \ "email").as[String]
val password = (request.body \ "password").as[String]
Ok(Json.toJson(
Map("status" -> "OK",
"message" -> "%s created".format(email))
))
}
In tests
"login" in new WithApplication{
val request = route( FakeRequest(
Helpers.POST,
controllers.routes.ApplicationCtrl.login.url,
FakeHeaders(Seq(CONTENT_TYPE -> Seq("application/json"))),
""" {"email" : "bob@mail.com", "password" : "secret"} """
)).get
status(request) must equalTo(OK)
}
When I test using command line:
curl --header "Content-type: application/json" --request POST --data '{"email" : "bob@mail.com", "password" : "secret"}' http://localhost:9000/login
It gets desirable response.
{"status":"OK","message":"bob@mail.com created"}
But the test returns 400 error.
What's wrong?
(command line test wins by simplicity and understandability)
What's happening here is that Play sets the content type of the request according to the type of the body. You're using a string body so that the content type header you're setting is later overridden by text/plain; charset=utf-8
.
Because you're explicitly parsing the body as Json the body parser will return a bad request 403 if the content type is not either text/json
or application/json
.
The best thing to do in your case is to use a Json body, i.e:
"login" in new WithApplication {
val request = route( FakeRequest(
POST,
controllers.portal.routes.Portal.test.url,
FakeHeaders(Seq.empty),
play.api.libs.json.Json.obj("email" -> "bob@mail.com", "password" -> "secret")
)).get
status(request) must equalTo(OK)
}
Note that you can make that a bit more succinct by letting an alternate FakeRequest
constructor infer the method and URL of your action from the call:
val request = route(FakeRequest(controllers.portal.routes.Portal.test)
.withBody(Json.obj("email" -> "bob@mail.com", "password" -> "secret"))).get
Data types you can use as the body parameter and their content type mapping:
JsValue
-> application/json
NodeSeq
-> text/xml
String
-> text/plain
Map[String, Seq[String]]
-> application/x-www-form-urlencoded
Array[Byte]
-> nothing
There's also the option of using the tolerantJson
as a body parser to skip checking the content type completely.