Barry
Barry

Reputation: 1820

Decode gzipped JSON in Akka HTTP

I have an endpoint we can call /test that internally fetches data from a 3rd party API and then wants to do some transformation before returning a response. Where I am hung up is this 3rd party API is returning gzipped JSON and I can't decode it (yet). I found the decodeRequest directive but it seems I have to use this in my routing and I am a level deeper here. I have an internal method I call once I receive a GET to my endpoint /test which is named do3rdPartyAPIRequest where I build up an HttpRequest and pass to Http().singleRequest() so then in return I have a Future[HttpResponse] which is where I think I want to be but I am stuck here.

With some local APIs I built and consumed in a similar fashion I didn't encode my responses so typically with a Future[HttpResponse] I check the response status and go into conversion to JSON via Unmarshal but this one needs an extra step as far as I know before transforming to JSON. I realize this question is very similar to this one however that is spray specific and I haven't been able to translate this answer into current akka http

Upvotes: 2

Views: 1900

Answers (2)

jfuentes
jfuentes

Reputation: 487

You would expect that compressed content would be managed by akka-http by default, but it only provides PredefinedFromEntityUnmarshallers and inside entity there is not information about Content-encoding header.

To solve this you have to implement your own Unmarshaller and to have it in scope

Example:

implicit val gzipMessageUnmarshaller = Unmarshaller(ec => {
      (msg: HttpMessage) => {
        val `content-encoding` = msg.getHeader("Content-Encoding")
        if (`content-encoding`.isPresent && `content-encoding`.get().value() == "gzip") {
          val decompressedResponse = msg.entity.transformDataBytes(Gzip.decoderFlow)
          Unmarshal(decompressedResponse).to[String]
        } else {
          Unmarshal(msg).to[String]
        }
      }
    }) 

Upvotes: 3

Barry
Barry

Reputation: 1820

Finally figured this out - this might not be the absolute best to get a bytestring from response but it works .. Turns out you can use Gzip class

and you have two options

  1. Gzip.decode
  2. Gzip.decoderFlow

Here are my examples in case this helps you:

def getMyDomainObject(resp: HttpResponse):Future[MyDomain] = {
 for {
   byteString <- resp.entity.dataBytes.runFold(ByteString(""))(_ ++ _)
   decompressedBytes <- Gzip.decode(byteString)
   result <- Unmarshal(decompressedBytes).to[MyDomain]
  } yield result
}


def getMyDomainObjectVersion2(resp:HttpResponse):Future[MyDomain] = {
   resp.entity.dataBytes
   .via(Gzip.decoderFlow)
   .runWith(Sink.head)
   .flatMap(Unmarshal(_).to[MyDomain])
}

Upvotes: 7

Related Questions