How to unmarshall akka http request entity as string?

Ixx picture Ixx · Jun 15, 2015 · Viewed 12.7k times · Source

I'm trying to unmarshall request payload as string, but for some reason it's failing. My code:

path("mypath") {

  post {
    decodeRequest {
      entity(as[String]) {jsonStr => //could not find implicit value for...FromRequestUnmarshaller[String]
        complete {
          val json: JsObject = Json.parse(jsonStr).as[JsObject]
          val jsObjectFuture: Future[JsObject] = MyDatabase.addListItem(json)
          jsObjectFuture.map(_.as[String])
        }
      }          
    }
  }
}

In this SO thread for example it seems to be that this implicit should be available by default. But maybe this is different in akka-http?

I tried importing akka.http.scaladsl.unmarshalling.PredefinedFromEntityUnmarshallers which has a stringUnmarshaller but it doesn't help. Maybe because this returns type FromEntityUnmarshaller[String] not FromRequestUnmarshaller[String]. There's also a string unmarshaller in spray.httpx.unmarshalling.BasicUnmarshallers but this also doesn't help, neither akka.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers

How can I unmarshall (and marshall) into a string?

(Bonus: How to unmarshall directly in a JsObject (play json). But also only string as I'm interested in why this is not working and it may be useful for other cases).

Using 1.0-RC3

Thanks.

Answer

cmbaxter picture cmbaxter · Jun 15, 2015

Your code should be okay provided you have the right implicits in scope. If you have an implicit FlowMaterializer in scope then things should work as expected as this code that compiles shows:

import akka.http.scaladsl.server.Route
import akka.actor.ActorSystem
import akka.stream.ActorFlowMaterializer
import akka.http.scaladsl.model.StatusCodes._
import akka.http.scaladsl.server.Directives._
import akka.stream.FlowMaterializer

implicit val system = ActorSystem("test")
implicit val mater = ActorFlowMaterializer()

val routes:Route = {
  post{
    decodeRequest{
      entity(as[String]){ str =>
        complete(OK, str) 
      }
    }
  }    
}

If you wanted to take things a step further and unmarshall to a JsObject then you just need an implicit Unmarshaller in scope to handle that conversion, something like this:

implicit val system = ActorSystem("test")
implicit val mater = ActorFlowMaterializer()

import akka.http.scaladsl.unmarshalling.Unmarshaller
import akka.http.scaladsl.model.HttpEntity

implicit val um:Unmarshaller[HttpEntity, JsObject] = {
  Unmarshaller.byteStringUnmarshaller.mapWithCharset { (data, charset) =>
    Json.parse(data.toArray).as[JsObject]
  }    
}  

val routes:Route = {
  post{
    decodeRequest{
      entity(as[String]){ str =>
        complete(OK, str) 
      }
    }
  } ~
  (post & path("/foo/baz") & entity(as[JsObject])){ baz =>
    complete(OK, baz.toString)
  }    
}