mixel mixel - 2 months ago 53
Scala Question

http4s - get request body as String or InputStream

I'm trying to define

HttpService
that receives json and parses it to case class with
json4s
library:

import org.http4s._
import org.http4s.dsl._
import org.json4s._
import org.json4s.native.JsonMethods._

case class Request(firstName: String, secondName: String)

HttpService {
case req @ POST -> Root =>
val request = parse(<map req.body or req.bodyAsText to JsonInput>).extract[Request]
Ok()
}


How can I get
org.json4s.JsonInput
from
req.body
or
req.bodyAsText
?

I know that
json4s
also have
StringInput
and
StreamInput
that inherits from
JsonInput
for using with
String
and
InputStream
so I think that I need to convert
req.body
to
InputStream
or
req.bodyAsText
to
String
but I still do not understand how.

I'm new to Scala and I do not yet fully understand some concepts such as
scalaz.stream.Process
.

Answer

You can use the http4s-json4s-jackson (or http4s-json4s-native) packages and use an org.http4s.EntityDecoder to easily get a Foo (I renamed your Request case class to Foo below) from a request.

EntityDecoder is a type class which can decode an entity from the request body. We want to get the Foo posted in JSON, so we need to create an EntityDecoder[Foo] which can decode JSON. If we want to create this decoder using json4s we need a Reader (or a JsonFormat).

If you have an EntityDecoder[Foo] instance, we can get the Foo from the request with req.as[Foo].

import org.json4s._
import org.json4s.jackson.JsonMethods._

import org.http4s._
import org.http4s.dsl._
import org.http4s.json4s.jackson._

case class Foo(firstName: String, secondName: String)

// create a json4s Reader[Foo]
implicit val formats = DefaultFormats
implicit val fooReader = new Reader[Foo] { 
  def read(value: JValue): Foo = value.extract[Foo] 
}
// create a http4s EntityDecoder[Foo] (which uses the Reader)
implicit val fooDec = jsonOf[Foo]

val service = HttpService {
  case req @ POST -> Root => 
    // req.as[Foo] gives us a Task[Foo]
    // and since Ok(...) gives a Task[Response] we need to use flatMap
    req.as[Foo] flatMap ( foo => Ok(foo.firstName + " " + foo.secondName) )
}

Note: The json libraries libraries used most often with http4s are probably argonaut and circe. So you might find more http4s examples using one of those libraries.