How do I access post data from scala play? - scala

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("");
}

Related

Add custom BodyParser to non class Action

I am building an Application using PlayFramework / Scala.
For my security layer I am using Auth0 which is working fine for the main page, and I am already able to get profile information / add new users etc.
Now I have an API and I want to let people use it only when they are connected as well so I added this custom Action on my API controller :
def AuthenticatedAction(f: Request[AnyContent] => Future[Result]): Action[AnyContent] = {
Action.async { implicit request =>
(request.session.get("idToken").flatMap { idToken =>
cache.get[JsValue](idToken + "profile")
} map { profile =>
f(request) // IF USER CONNECTED THEN ADD PROFILE TO REQUEST AND PROCEED
}).orElse {
Some(Future(Redirect("/login"))) // OTHERWISE REDIRECT TO LOGIN PAGE
}.get
}
}
I am able to use it for my read action (returning only one record by ID) :
def read(entityName: String, id: String) = AuthenticatedAction {
// SOME NOT RELEVANT CODE
}
My problem comes when I try to send json to create an object :
This was my code working before I tried to add a custom authenticated action :
def store(entityName: String, id: String) = Action.async(parse.json) {
// SOME NOT RELEVANT CODE
}
Now I was expecting to use
def store(entityName: String, id: String) = AuthenticatedAction(parse.json) {
// SOME NOT RELEVANT CODE
}
Here is the compile error :
type mismatch; found : play.api.mvc.BodyParser[play.api.libs.json.JsValue]
required: play.api.mvc.Request[play.api.mvc.AnyContent] ⇒ scala.concurrent.Future[play.api.mvc.Result]
And I know it comes from the fact that I do not support passing custom body parsers, I have looked into using ActionBuilder from the docs as well but I am trying to use the code provided by Auth0.
Is there a way to handle custom parsers when the custom action is not defined as a class ?
So I just found a solution !
I noticed Action async has actually 2 different signatures :
async[A](bodyParser: BodyParser[A])(block: (R[A]) ⇒ Future[Result]): Action[A]
and
async(block: ⇒ Future[Result]): Action[AnyContent]
So I just added another AuthenticationAction on my part
def AuthenticatedAction[A](b: BodyParser[A])(f: Request[A] => Future[Result]): Action[A] = {
Action.async(b) { implicit request =>
(request.session.get("idToken").flatMap { idToken =>
cache.get[JsValue](idToken + "profile")
} map { profile =>
f(request) // IF USER CONNECTED THEN ADD PROFILE TO REQUEST AND PROCEED
}).orElse {
Some(Future(Redirect("/login")))
}.get
}
}

Secured trait causing spec2 unit test compilation errors

I've implemented authorization for my Play Framework (version 2.3.5) application as per the official security documentation:
trait Secured {
def username(request: RequestHeader) = request.session.get(Security.username)
def onUnauthorized(request: RequestHeader) = Results.Redirect(routes.Login.index)
def withAuth(f: => String => Request[AnyContent] => Result) = {
Security.Authenticated(username, onUnauthorized) { user =>
Action(request => f(user)(request))
}
}
}
However, when I decorate my controller actions with the withAuth function like so:
def export = withAuth { username => implicit request =>
val csv = csv()
Ok(csv)
.as("text/csv")
.withHeaders(
CONTENT_DISPOSITION -> ("attachment; filename=export.csv"),
CONTENT_LENGTH -> csv.length.toString
)
}
I get compilation errors in my controller specs2 unit tests:
"export data as CSV" in {
val controller = new controllers.DataController
val result = controller.export().apply(FakeRequest())
headers(result) must havePair("Content-Disposition" -> ("attachment; filename=export.csv"))
}
The call to the headers test helper function fails with the following compiler error message:
type mismatch; found : play.api.libs.iteratee.Iteratee[Array[Byte],play.api.mvc.Result] required: scala.concurrent.Future[play.api.mvc.Result]
Am I doing something wrong? I've tried some of the remedies suggested by other Stackoverflow users but they all seem to rely on setting the body type, but my actions do not have a body type.
It looks as though the call to withAuth obscures some of the types of the Action being wrapped so maybe there is a more type-safe way of expressing the Secured trait?
Try changing this:
val result = controller.export().apply(FakeRequest())
into this:
val result = controller.export().apply(FakeRequest()).run
And then it should work.

How to init case class from one JSON string instead of separate fields of form

New to the Play framework.
After reading the doc, I know the standard way to initialize case class by using form like this:
import play.api.data._
import play.api.data.format.Formats._
val userForm = Form(
mapping(
"name" -> of[String],
"age" -> of[Int],
"email" -> of[String]
)(User.apply)(User.unapply)
)
in this case, we provide separate textboxes for user to fill in in the web interface, after submit, the server code will init User according to the form. Works Great!
but, what if what I wanna provide is only one textbox, and user(actually the mobile guy who wanna test the web service API) should fill in a complete JSON string and then post it to server to create a User instance?
Would appreciate if you can provide both the view & controller code.
Taking your question literally (creating an object via JSON from a form field), it could look something like below. For completeness I've also provided the direct (and much simpler) way to unmarshall an object from JSON in a controller action (using content type application/json instead of application/x-form-urlencoded).
To extract a custom type (in your case a User object) from a form field you need to write a mapper to bind/unbind the data to/from a string. The userJsonMapper in the User's companion object does this, giving an error (error.jsonObj, for which you can provide an i18n translation) if JSON parsing fails.
The model code app/models/User.scala:
package models
case class User(name: String, age: Int, email: String)
object User {
// Create a generic JSON format using the macro. NB: This
// does *NOT* include validation of the email or age fields.
import play.api.libs.json.{Format, Json}
implicit val format: Format[User] = Json.format[User]
// Create a form and a field binder that validates the contents
// of the "json" field to ensure it is valid JSON for a user.
import play.api.data.Form
import play.api.data.Forms._
import play.api.data.FormError
import play.api.data.format.Formatter
// The actual form object
val jsonForm: Form[User] = Form(
single("json" -> of(userJsonMapper))
)
// Map a User object to/from a form field
def userJsonMapper: Formatter[User] = new Formatter[User] {
def bind(key: String, data: Map[String, String]) = {
play.api.data.format.Formats.stringFormat.bind(key, data).right.flatMap { s =>
scala.util.control.Exception.allCatch[User]
.either(Json.parse(s).as[User])
.left.map(e => Seq(FormError(key, "error.jsonObj", Nil)))
}
}
def unbind(key: String, value: User) = Map(key -> Json.stringify(Json.toJson(value)))
}
}
The template code app/views/createUserForm.scala.html:
#(form: Form[User], action: Call)(implicit request: RequestHeader)
<html>
<body>
#helper.form(action = action, 'role -> "form") {
#helper.inputText(form("json"))
<button type="submit">Create User</button>
}
</body>
</html>
The route file conf/routes:
GET /createUser controllers.Users.createUser
POST /createUser controllers.Users.createUserPost
# For good measure, include an action to create
# a user directly from JSON via a JSON content-type.
POST /createUserJson controllers.Users.createUserPostJson
The controller code app/controllers/Users.scala:
package controllers
import play.api.mvc._
import models.User
object Users extends Controller {
def createUser = Action { implicit request =>
Ok(views.html.createUserForm(
User.jsonForm,
controllers.routes.Users.createUserPost()
))
}
def createUserPost = Action { implicit request =>
User.jsonForm.bindFromRequest.fold(
// Form data is bad - re-render form.
errForm => BadRequest(views.html.createUserForm(
errForm,
controllers.routes.Users.createUserPost()
)),
user =>
// Create user here
Ok(s"User: $user")
)
}
}
// A more direct way of doing this is to POST the data
// as application/json and unmarshall it directly.
def createUserPostJson = Action(parse.json) { implicit request =>
request.body.validate[User].fold(
errs => BadRequest(play.api.libs.json.JsError.toFlatJson(errs)),
user => Ok(s"User: $user")
)
}
After reading this thread: Scala Play form validation: different forms for one case class - is it possible?, figured out a way to do what I want.
The canonical way of extracting fields from form to create an instance is like this:
import play.api.data._
import play.api.data.format.Formats._
val userForm = Form(
mapping(
"name" -> of[String],
"des" -> of[String],
...
)(User.apply)(User.unapply)
)
here we create a form and then bind the target object's apply and unapply methods to it, thanks to these methods, the form now has the ability to extract fields and turn them into Object.
What I wanna do is a form which can turn a JSON string(instead of separate fields) into an object, then why not provide the form with functions like this:
json:String => User
yes, additional to User's apply&unapply function, we add:
def applyStr(jsonStr: String): User = {
val json: JsValue = Json.parse(jsonStr)
val validateResult: JsResult[User] = json.validate[User](UserReads)
validateResult match{
case s: JsSuccess[User] => s.get
case e: JsError => null
}
}
def unapplyToStr(user: User): Option[String] = {
Some(Json.toJson(user).toString())
}
and create a form like this:
val userJSONForm = Form(
mapping(
"json" -> text
)(User.applyStr)(User.unapplyToStr)
)
now we have a form which can turn json string into User, what left is familiar, we create an action in controller to display form:
def createByJson() = Action {
Ok(views.html.user_json(userJSONForm))
}
the corresponding view is like:
#(userJsonForm: Form[User])
#helper.form(action = routes.UserController.saveJson()) {
#helper.textarea(userJsonForm("json"))
<input type="submit" />
}
after submit, the form will be delivered to UserController.saveJson():
def saveJson() = Action{implicit request =>
userJSONForm.bindFromRequest.fold(
formWithErrors => {
BadRequest(views.html.user_json(formWithErrors))
},
user => {
//do sth to the user
...
Ok("get user")
}
)
}

play framework - how can i call this function for the authenticated user in this code play2 scala zentasks

in the play2 scala zentasks sample application the code snippet looks like this
def IsAuthenticated(f: => String => Request[AnyContent] => Result) =
Security.Authenticated(username, onUnauthorized) { user =>
Action(request => f(user)(request))
}
What I need to do is call this function in model
def AddOnline(email: String, contact: Contact) = {
DB.withConnection { implicit connection =>
SQL(
"""
update contact
set online_status = {'online'} //I want to write the value contact.status=online
where email = {email}
"""
).on(
'email -> contact.email,
'online_status -> contact.online_status
).executeUpdate()
}
}
But my challenge here is to call this AddOnline function every time a certain user is authenticated by the above piece of code. Can someone suggest how I should go about this? I am a novice in this and I am going round and round in circles without making any progress
You can call the addOnline method from the IsAuthenticated like this:
def IsAuthenticated(f: => String => Request[AnyContent] => Result) =
Security.Authenticated(username, onUnauthorized) { user =>
// Do what you need to do here before the action is called
Contact.addOnline(user)
Action(request => f(user)(request))
}
Please note that the user is not the user object but just the email of the authenticated user.
Also, as you are only adding the online status of the user it looks like you can simplify your AddOnline method to something like this.
def addOnline(email: String) = {
DB.withConnection { implicit connection =>
SQL(
"""
update contact
set online_status = 'online'
where email = {email}
"""
).on(
'email -> email,
).executeUpdate()
}

Overloaded method error in Scala Play Framework

I am writing a method to set the session cookie properly after a user authenticates. Setting the cookies as follows does not work. The following snippet results in an error with the 'withSession' call:
Overloaded method value [withSession] cannot be applied to ((String, Long))
Code:
/**
* Process login form submission.
*/
def authenticate = Action { implicit request =>
loginForm.bindFromRequest.fold(
formWithErrors => BadRequest(views.html.login(formWithErrors)),
credentials => {
val user = models.User.findByEmail(credentials._1)
user match {
case Some(u) => {
Redirect(routes.Dashboard.index).withSession(Security.username -> u.id)
}
case None => Redirect(routes.Auth.login)
}
}
)
}
'credentials' is simply a tuple with the email and password submitted by the user. If I get rid of 'withSession' part then it runs fine. If I move the 'Redirect' statement out of the pattern matching code then is works fine. Why does it not work as I have it above and how do I fix it?
I think withSession expects a String, String
Have a look at http://www.playframework.org/documentation/api/2.0.3/scala/index.html#play.api.mvc.SimpleResult
def withSession(session: (String, String)*): PlainResult = withSession(Session(session.toMap))