Play Framework. Accepting JSON and Form Submit on same action

1.6k views Asked by At

I have written this sample Application using Play Scala framework. (TypeSafe Activator)

Application.scala

package controllers

import play.api.mvc._
import models._


object Application extends Controller {

  def index = Action {
        Ok(views.html.main(Person.form))
  }

  def submit = Action { implicit request =>
    Person.form.bindFromRequest().fold(
            formWithErrors => BadRequest(views.html.main(formWithErrors)),
            p => {
                Ok(s"Hi ${p.firstname} ${p.lastname}")
            }
        )
  }
}

Person.scala

package models

import play.api.data.Form
import play.api.data.Forms.{mapping, text, of}
import play.api.data.format.Formats.doubleFormat
import play.api.libs.json.{__, Reads, Writes}
import play.api.libs.functional.syntax._
import play.api.libs.json.Json

case class Person(firstname: String, lastname: String)

object Person {
    val form = Form(mapping(
          "firstname" -> text,
          "lastname" -> text
        )(Person.apply)(Person.unapply))

  implicit val readsPerson: Reads[Person] = (
    ((__ \ "firstname").read[String]) and
    ((__ \ "lastname").read[String])
  )(Person.apply _)  

  implicit val writesItem = Writes[Person] {
    case Person(firstname, lastname) =>
      Json.obj(
        "firstname" -> firstname,
        "lastname" -> lastname
      )
  }  
}

Now this works wonderfully from the browser.

However when I try to invoke the action from curl

curl -H "Content-Type: application/x-www-form-urlencoded" -X POST -d '{"firstname":"abhishek", "lastname":"srivastava"}' http://localhost:9000/

It fails with a lengthy HTML message.

My understanding was that if I use x-www-form-urlencoded, then at the time of post, I can accept requests both from the HTML form as well as a JSON request.

Am I doing something wrong? or is my understanding flawed?

Basically I want the same controller method Action to service the json post request as well as the html form submission.

1

There are 1 answers

3
bjfletcher On BEST ANSWER

Short answer: set the Content-Type header on the client side, use the default body parser on the server side, and it'll all work just like magic! :) Replace Action(parse.urlFormEncoded) with simply Action which'll then use the default parser.

Long answer:

The default parser will automatically use the correct parser based on the Content-Type header, whether it's application/x-www-form-urlencoded, application/json and some more.

Then in your post method, bindFromRequest will then examine all bodies determining which one has been set, whether it's asFormUrlEncoded or asJson, then use it for form data handling.