meucaa
meucaa

Reputation: 1505

How to get akka.http.scaladsl.model.HttpRequest body?

I am using akka-http and writing an Unmarhsaller for one of my classes. What I am trying to do is to get the body of the POST request as a String so I can create my object with it :

case class MyClass(geom: String)

implicit def fromRequestUnmarshaller = Unmarshaller[HttpRequest, MyClass]({implicit ec: ExecutionContext =>
    req: HttpRequest => Future(MyClass(req.entity.asInstanceOf[HttpEntity.Strict].data.map(_.toChar).mkString))
})

It seems like a very complicated line of code just to get the body as a String. Plus, I am doing a very ugly asInstanceOf[HttpEntity.Strict] just because I figured the HttpRequest was of this type while debugging.

My question: is there a simpler/cleaner way to achieve my goal ?

Thanks a lot :)

Upvotes: 3

Views: 1834

Answers (1)

Be Careful

The reason akka implements the entity as a Source[ByteString,_] is because the entity of an HttpRequest can potentially be infinite length. Therefore, you better make sure that your application has enough RAM to handle any request that is likely to be thrown at it...

toStrict

You can use HttpEntity#toStrict:

implicit val materializer : Materializer = ???
implicit val executionContext : ExecutionContext = ???    

val entityFromRequest : (HttpRequest, FiniteDuration) => Future[ByteString] = 
  (_ : HttpRequest)
    .entity 
    .toStrict(_ : FiniteDuration)
    .map(_.data)

Manual Conversion

You can access the "body", i.e. entity, of an HttpRequest as a Source[ByteString, _]:

val getBodySource : HttpRequest => Source[ByteString,_] = 
  _.entity
   .dataBytes

This Source can then be sent to a Sink which collects the ByteString values into a Seq:

val convertSrcToSeq : Source[ByteString,_] => Future[Seq[ByteString]] = 
  _ runWith Sink.seq

The body you are looking for is one continuous String therefore these ByteStrings need to be reduced to a single value:

val reduceSeqToStr : Future[Seq[ByteString]] => Future[ByteString] = 
  _ map (_ reduceOption (_ ++ _) getOrElse ByteString.empty)

These steps can now be composed into a single function:

val getBodyStrFromRequest : HttpRequest => Future[ByteString] = 
  getBodySource andThen convertSrcToSeq andThen reduceSeqToStr

Upvotes: 3

Related Questions