Entity handling
Why Entity*
Http4s handles HTTP requests and responses in a streaming fashion. Your service
will receive a request after the header has been parsed (ok, not 100%
streaming), but before the body has been fully received. The same applies to the
http client usage, where you can start a connection before the body is fully
materialized. You don’t have to load the full body into memory to submit the
request either. Taking a look at Request and Response, both have a body of
type EntityBody, which is simply an alias to Process[Task, ByteVector]. To
understand Process, take a look at the streams-tutorial.
The EntityDecoder and EntityEncoder help with the streaming nature of the
data in a http body, and they also have additional logic to deal with media
types. Not all decoders are streaming, depending on the implementation.
Construction and Media Types
Entity*s also encode which media types they correspond to. The
EntityDecoders for json expect application/json. To implement this
functionality, the constructor EntityDecoder.decodeBy uses MediaRanges. You
can pass multiple as needed. You can also append functionality to an existing
one via EntityDecoder[T].map - however, you can’t change the media
type in that case.
When you encode a body with the EntityEncoder for json, it appends the
Content-Type: application/json header. You can construct new encoders via
EntityEncoder.encodeBy or reuse an already existing one via
EntityEncoder[T].contramap and withContentType.
See the MediaRange companion object for ranges, and MediaType for specific
types. Because of the implicit conversions, you can also use (String, String)
for a MediaType.
By default, decoders content types are ignored since it could lead to unexpected runtime errors.
Chaining Decoders
Decoders’ content types are used when chaining decoders with orElse in order to
determine which of the chained decoders are to be used.
scala> import org.http4s._
import org.http4s._
scala> import org.http4s.dsl._
import org.http4s.dsl._
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> sealed trait Resp
defined trait Resp
scala> case class Audio(body: String) extends Resp
defined class Audio
scala> case class Video(body: String) extends Resp
defined class Video
scala> val response = Ok().withBody("").withContentType(Some(MediaType.`audio/ogg`))
response: scalaz.concurrent.Task[org.http4s.Response] = scalaz.concurrent.Task@79c04964
scala> val audioDec = EntityDecoder.decodeBy(MediaType.`audio/ogg`) { msg =>
| EitherT {
| msg.as[String].map(s => Audio(s).right[DecodeFailure])
| }
| }
audioDec: org.http4s.EntityDecoder[Audio] = org.http4s.EntityDecoder$$anon$3@5ae0958
scala> val videoDec = EntityDecoder.decodeBy(MediaType.`video/ogg`) { msg =>
| EitherT {
| msg.as[String].map(s => Video(s).right[DecodeFailure])
| }
| }
videoDec: org.http4s.EntityDecoder[Video] = org.http4s.EntityDecoder$$anon$3@485bf268
scala> val bothDec = audioDec.widen[Resp] orElse videoDec.widen[Resp]
bothDec: org.http4s.EntityDecoder[Resp] = org.http4s.EntityDecoder$OrDec@640d5be8
scala> println(response.as(bothDec).run)
<console>:27: warning: method run in class Task is deprecated (since 7.2): use unsafePerformSync
println(response.as(bothDec).run)
^
Audio()
Presupplied Encoders/Decoders
The EntityEncoder/EntityDecoders shipped with http4s.
Raw Data Types
These are already in implicit scope by default, e.g. String, File,
Future[_], and InputStream. Consult EntityEncoder and EntityDecoder for
a full list.
JSON
With jsonOf for the EntityDecoder, and jsonEncoderOf for the EntityEncoder:
- argonaut:
"org.http4s" %% "http4s-argonaut" % Http4sVersion - circe:
"org.http4s" %% "http4s-circe" % Http4sVersion - json4s-native:
"org.http4s" %% "http4s-json4s-native" % Http4sVersion - json4s-jackson:
"org.http4s" %% "http4s-json4s-jackson" % Http4sVersion
XML
For scala-xml (xml literals), import org.http4s.scalaxml. No direct naming
required here, because there is no Decoder instance for String that would
cause conflicts with the builtin Decoders.
- scala-xml:
"org.http4s" %% "http4s-scala-xml" % Http4sVersion
Twirl
If you’re working with twirl templates, there’s a bridge for that too:
- scala-twirl:
"org.http4s" %% "http4s-twirl" % Http4sVersion