在Akka HTTP中解码gzip压缩的JSON

时间:2016-08-17 19:02:51

标签: json scala akka-http

我有一个端点,我们可以调用/test,在内部从第三方API获取数据,然后在返回响应之前进行一些转换。我挂断的地方是这个第三方API正在返回gzip压缩的JSON,我还无法解码它(还)。我找到了decodeRequest directive,但似乎我必须在我的路由中使用它,我在这里更深层次。我有一个内部方法,一旦我收到GET到我的端点/test,其名为do3rdPartyAPIRequest,我在其中建立一个HttpRequest并传递给Http().singleRequest()所以作为回报,我有Future[HttpResponse]这是我认为我想成为的地方,但我被困在这里。

对于我以类似方式构建和使用的一些本地API,我没有编码我的响应,因此通常使用Future[HttpResponse]我检查响应状态并通过Unmarshal转换为JSON但是这样在转换为JSON之前,我需要一个额外的步骤。我意识到这个问题与this one非常相似,但这是喷涂特定的,我无法将此答案转换为当前的akka​​ http

2 个答案:

答案 0 :(得分:6)

最后想出来了 - 这可能不是从响应中获取字节串的绝对最佳但它可以工作..原来你可以使用Gzip class

你有两个选择

  1. Gzip.decode
  2. Gzip.decoderFlow
  3. 以下是我的示例,以防这对您有所帮助:

    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])
    }
    

答案 1 :(得分:2)

您可能希望压缩内容默认由akka-http管理,但它只提供PredefinedFromEntityUnmarshallers,而实体内部没有Content-encoding标头的信息。

要解决此问题,您必须实现自己的Unmarshaller并将其纳入范围

示例:

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]
        }
      }
    })