Bastl
Bastl

Reputation: 2996

Appending to given formdata for http post

I am calling the following helper function from an action to make a syncronous HTTP-post with a special key-value pair appended to the URL-parameters in formdata:

  def synchronousPost(url: String, formdata: Map[String, Seq[String]], timeout: Duration=Duration.create(30, TimeUnit.SECONDS)): String = {
    import ExecutionContext.Implicits.global
    val params = formdata.map { case (k, v) => "%s=%s".format(k, URLEncoder.encode(v.head, "UTF-8")) }.mkString("&")
    val future: Future[WSResponse] = ws.url(url).
       withHttpHeaders(("Content-Type", "application/x-www-form-urlencoded")).
       post(params)
    try {
      Await.result(future, timeout)
      future.value.get.get.body
    } catch {
      case ex: Exception =>
        Logger.error(ex.toString)
        ex.toString
    }
  }

It is called like this:

  def act = Action { request => 
    request.body.asFormUrlEncoded match {
      case Some(formdata) =>
        synchronousPost(url, formdata + ("handshake" -> List("ok"))) match {

Actually it is copy-pasted from some old gist and I am quite sure it can be rewritten in a cleaner way.

How can the line val params = ... be rewritten in a cleaner way? It seems to be low level.

The original formdata comes in from request.body.asFormUrlEncoded and I simply need to append the handshake parameter to the formdata-map and send the original request back to the sender to do the handshake.

Upvotes: 0

Views: 252

Answers (1)

Mikesname
Mikesname

Reputation: 8901

Since formdata is a Map[String, Seq[String]], a data type for which a default WSBodyWritable is provided, you can simply use it as the request body directly:

val future: Future[WSResponse] = ws.url(url)
  .withHttpHeaders(("Content-Type", "application/x-www-form-urlencoded"))
  .post(formdata)

Incidentally, it's considered bad form to use Await when it's easy to make Play controllers return a Future[Result] using Action.async, e.g:

def asyncPost(url: String, formdata: Map[String, Seq[String]]): Future[String] = {
  ws.url(url)
    .withHttpHeaders(("Content-Type", "application/x-www-form-urlencoded"))
    .post(formdata)
    .map(_.body)
}

def action = Action.async { request =>
  request.body.asFormUrlEncoded match {
    case Some(formdata) =>
      asyncPost(url, formdata + ("handshake" -> List("ok"))).map { body =>
        Ok("Here is the body: " + body)
      } recover { 
        case e => 
          Logger.error(e)
          InternalServerError(e.getMessage)
      }
    case None => Future.successful(BadRequest("No form data given"))
  }
}

Upvotes: 1

Related Questions