I'm trying to go something really simple with Playframework Scala (2.3): create a route POST and get POST parameters.
The route definition
POST /ff/user controllers.Application.createUser
The controller
def createUser = Action.async { request =>
val user = request.queryString.get("user").flatMap(_.headOption)
val email = request.queryString.get("email").flatMap(_.headOption)
val firstname = request.queryString.get("firstname").flatMap(_.headOption)
val lastname = request.queryString.get("lastname").flatMap(_.headOption)
Logger.debug("Create User")
Logger.debug(s"user=$user")
Logger.debug(s"email=$email")
Ok("Youpi")
}
When I post a request to /ff/user, the log says : user=None, email=None.
I cannot figure out why they are "None". What is wrong?
Thank for helping.
When using a POST like this, you probably want to look at the body field on the request parameter, which will contain the form that was posted. You usually don't use a query string with POST requests (more about that here). So, that might look like:
def createUser = Action.async { request =>
val user = request.body.asFormUrlEncoded.get.get("user").head
Future(Ok())
}
You might also want to use the Action.async overload that provides a parsed body. For example, it might look like:
def createUser = Action.async(parse.urlFormEncoded) { request =>
//body is already treated as a Map[String, Seq[String]] because of the parameter passed to async
val user = request.body("user").head
Future(Ok())
}
Related
Not sure if I'm getting this whole routing DSL thing right but here's the question. I want to do a post to external service such as:
val post = pathPrefix("somePath") {
post {
//get the response mapped to my Output object
}
}
Then I want the response (which is a Json) to be mapped to an object matching the fields for example Output (assuming I have my JsonProtocol set up). How is this done?
You are using HTTP server directives to "retrieve" something "externally". This is what typically an HTTP client does.
For this sort of things, you can use akka http client api.
For example:
val response = Http().singleRequest(HttpRequest(uri = "http://akka.io"))
response onComplete {
case Success(res) =>
val entity = Unmarshal(res.entity).to[YourDomainObject]
// use entity here
case Failure(ex) => // do something here
}
However, this requires some Unmarshaller (to deserialize the received json). Take also a look at Json Support, as it helps you define marshallers easily:
case class YourDomainObject(id: String, name: String)
implicit val YourDomainObjectFormat = jsonFormat2(YourDomainObject)
I think what you are trying to ask is how to get the body i.e in JSOn format to the Case class that you have
Here is a quick example:
path("createBot" / Segment) { tag: String =>
post {
decodeRequest {
entity(as[CaseClassName]) { caseclassInstance: CaseClassName =>
val updatedAnswer = doSomeStuff(caseclassInstance)
complete {
"Done"
}
}
}
You can find more detailed example from here : https://github.com/InternityFoundation/Stackoverflowbots/blob/master/src/main/scala/in/internity/http/RestService.scala#L56
I hope it answers your question.
I have a controller that is protected by an Authentication trait. The trait looks like this:
def isAuthenticated(f: => AccountDTO => Request[AnyContent] => Result) =
Security.Authenticated(username, onUnauthorized) { user =>
Action.async {
request => {
Future.successful(f(user)(request))
}
}
}
Everything works well when doing a normal request, but when doing unit tests I encounter problems.
I create the following fake request:
val goodRequest = FakeRequest("POST", "/platform/api/v1/files")
.withBody(Json.toJson(ScalaMockingUtil.fileValidMetadataJson))
.withHeaders((HeaderNames.AUTHORIZATION, "4322tertf2643t34t34"))
Next, I get my controller object and call the method by applying that FakeRequest:
val result: Iteratee[Array[Byte], Result] = filesController.createFileMetadata()(goodRequest)
The problem that I am facing is that somewhere along the line the FakeReuqest gets down-casted to RequestHeader. The problem seems to be the one described here: Unable to test controller using Action.async where the Action has two apply methods instead of one. however, I can't seem to be able to force the one that I need.
Any help is appreciated.
The solution (or a workaround) was to use call() instead of apply():
val result = call(filesController.createFileMetadata(), goodRequest)
Now everything works as intended.
How would you implement String to json object unmarshaller for parameter of url-encoded POST ? I'm using version 1.2.
Here is what I want. Foursquare pushes url-encoded POST to my service. My route looks like this
path("handle_4sq_push") {
formFields("checkin".as[FsqCheckin], "user".as[String], "secret".as[String]) {
(checkin, user, secret) =>
complete {
StatusCodes.OK
}
}
}
I have json parser for FsqCheckin which is defined like this
implicit val fsqCheckinFormat = jsonFormat(FsqCheckin.apply, "id", "createdAt", "timeZoneOffset", "user", "venue")
So it's all good but it works only if parameters are form-encoded. Otherwise Spray says
There was a problem with the requests Content-Type:
Field 'checkin' can only be read from 'multipart/form-data' form content
So I thought I'd write unmarshaller. I wrote this
implicit def MyJsonUnmarshaller[T: RootJsonReader] =
Unmarshaller.delegate[String, T](ContentTypes.`*`) {
value => {
val json = JsonParser(value)
json.convertTo[T]
}
}
But if I bring it to scope of my route I get following compile error
too many arguments for method formFields: (fdm: spray.routing.directives.FieldDefMagnet)fdm.Out
formFields("checkin".as[FsqCheckin], "user".as[String], "secret".as[String]) {
^
It's the same error I have if I didn't have json parser for FsqCheckin in the scope.
How can I deal with this problem ?
Shockingly I figured it out myself. Here is working version of universal unmarshaller.
implicit def String2JsonParser[T: RootJsonReader] = new FromStringDeserializer[T] {
def apply(value: String) =
try
Right(JsonParser(value).convertTo[T])
catch {
case ex: Throwable => Left(spray.httpx.unmarshalling.MalformedContent(ex.getMessage))
}
}
I'm trying to write a filter similar to the simple one described in http://www.playframework.com/documentation/2.1.1/ScalaHttpFilters but I need to access the request body. The documentation below states that "when we invoke next, we get back an Iteratee. You could wrap this in an Enumeratee to do some transformations if you wished." I'm trying to figure out how to wrap the Iteratee so I can get the request body as a string within the filter so I can log that as well.
First thing you have to know is when the Filter is invoked, the request body is not parsed yet. That's why it's giving you a RequestHeader. You'll have to find out the type of body, and call the proper body parser accordingly.
You can find a example of body parsing in the CSRF filter (It can lookup for CSRF tokens in the first bytes of the request body).
See: https://github.com/playframework/playframework/blob/master/framework/src/play-filters-helpers/src/main/scala/csrf.scala#L221-L233.
Hope it helps.
I spent some time on this.
I am no means a Scala expert but this works pretty well! :)
object AccessLog extends EssentialFilter {
def apply(nextFilter: EssentialAction) = new EssentialAction {
def apply(requestHeader: RequestHeader) = {
val startTime = System.currentTimeMillis
nextFilter(requestHeader).map { result =>
val endTime = System.currentTimeMillis
val requestTime = endTime - startTime
val bytesToString: Enumeratee[ Array[Byte], String ] = Enumeratee.map[Array[Byte]]{ bytes => new String(bytes) }
val consume: Iteratee[String,String] = Iteratee.consume[String]()
val resultBody : Future[String] = result.body |>>> bytesToString &>> consume
resultBody.map {
body =>
Logger.info(s"${requestHeader.method} ${requestHeader.uri}" +
s" took ${requestTime}ms and returned ${result.header.status}")
val jsonBody = Json.parse(body)
Logger.debug(s"Response\nHeader:\n${result.header.headers.toString}\nBody:\n${Json.prettyPrint(jsonBody)}")
}
result.withHeaders("Request-Time" -> requestTime.toString)
}
}
}
The end result will print the body as a Json String (pretty printed).
In the controller method that routes to the action, simply call
Map<String, String[]> params = request().queryString();
This will get you a map of parameters, where you can then call
params.get("someParam")[0]
to get the param (if it is a single value). If the param is a list, ignore the indexing andthat will return an array.
I have a route that is type "POST". I am sending post data to the page. How do I access that post data. For example, in PHP you use $_POST
How do I access the post data in scala and play framework?
As of Play 2.1, there are two ways to get at POST parameters:
1) Declaring the body as form-urlencoded via an Action parser parameter, in which case the request.body is automatically converted into a Map[String, Seq[String]]:
def test = Action(parse.tolerantFormUrlEncoded) { request =>
val paramVal = request.body.get("param").map(_.head)
}
2) By calling request.body.asFormUrlEncoded to get the Map[String, Seq[String]]:
def test = Action { request =>
val paramVal = request.body.asFormUrlEncoded.get("param").map(_.head)
}
Here you've got good sample how it's done in Play:
https://github.com/playframework/Play20/blob/master/samples/scala/zentasks/app/controllers/Application.scala
val loginForm = Form(
tuple(
"email" -> text,
"password" -> text
) verifying ("Invalid email or password", result => result match {
case (email, password) => User.authenticate(email, password).isDefined
})
)
/**
* Handle login form submission.
*/
def authenticate = Action { implicit request =>
loginForm.bindFromRequest.fold(
formWithErrors => BadRequest(html.login(formWithErrors)),
user => Redirect(routes.Projects.index).withSession("email" -> user._1)
)
}
It's described in the documentation of the forms submission
as #Marcus points out, bindFromRequest is the preferred approach. For simple one-off cases, however, a field
<input name="foo" type="text" value="1">
can be accessed via post'd form like so
val test = Action { implicit request =>
val maybeFoo = request.body.get("foo") // returns an Option[String]
maybeFoo map {_.toInt} getOrElse 0
}
Here you've got good sample how it's done in Play 2:
def test = Action(parse.tolerantFormUrlEncoded) { request =>
val paramVal = request.body.get("param").map(_.head).getorElse("");
}