在 Akka HTTP 中解码 gzipped JSON
Decode gzipped JSON in Akka HTTP
我有一个可以调用 /test
的端点,它在内部从第三方 API 获取数据,然后想在 return 响应之前进行一些转换。我被挂断的地方是这个第 3 方 API 是 returning gzipped JSON 并且我无法解码它(还)。我找到了 decodeRequest directive 但似乎我必须在我的路由中使用它而且我在这里更深一层。我有一个内部方法,一旦我收到一个 GET
到我的端点 /test
,它被命名为 do3rdPartyAPIRequest
,我在其中建立一个 HttpRequest
并传递给 Http().singleRequest()
那么在 return 我有一个 Future[HttpResponse]
这是我想去的地方但我被困在这里了。
对于我以类似方式构建和使用的一些本地 API,我没有对我的响应进行编码,所以通常使用 Future[HttpResponse]
我检查响应状态并转换为 JSON 通过 Unmarshal
但据我所知,在转换为 JSON 之前,这需要一个额外的步骤。我意识到这个问题与 this one 非常相似,但是这是特定于喷雾的,我无法将这个答案翻译成当前的 akka http
终于弄明白了——这可能不是从响应中获取字节串的绝对最佳方法,但它确实有效。结果证明你可以使用 Gzip class
你有两个选择
Gzip.decode
Gzip.decoderFlow
以下是我的示例,以防对您有所帮助:
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])
}
您可能希望压缩内容默认由 akka-http 管理,但它只提供 PredefinedFromEntityUnmarshallers
并且在实体内部没有关于 Content-encoding
header 的信息.
要解决这个问题,您必须实现自己的 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]
}
}
})
我有一个可以调用 /test
的端点,它在内部从第三方 API 获取数据,然后想在 return 响应之前进行一些转换。我被挂断的地方是这个第 3 方 API 是 returning gzipped JSON 并且我无法解码它(还)。我找到了 decodeRequest directive 但似乎我必须在我的路由中使用它而且我在这里更深一层。我有一个内部方法,一旦我收到一个 GET
到我的端点 /test
,它被命名为 do3rdPartyAPIRequest
,我在其中建立一个 HttpRequest
并传递给 Http().singleRequest()
那么在 return 我有一个 Future[HttpResponse]
这是我想去的地方但我被困在这里了。
对于我以类似方式构建和使用的一些本地 API,我没有对我的响应进行编码,所以通常使用 Future[HttpResponse]
我检查响应状态并转换为 JSON 通过 Unmarshal
但据我所知,在转换为 JSON 之前,这需要一个额外的步骤。我意识到这个问题与 this one 非常相似,但是这是特定于喷雾的,我无法将这个答案翻译成当前的 akka http
终于弄明白了——这可能不是从响应中获取字节串的绝对最佳方法,但它确实有效。结果证明你可以使用 Gzip class
你有两个选择
Gzip.decode
Gzip.decoderFlow
以下是我的示例,以防对您有所帮助:
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])
}
您可能希望压缩内容默认由 akka-http 管理,但它只提供 PredefinedFromEntityUnmarshallers
并且在实体内部没有关于 Content-encoding
header 的信息.
要解决这个问题,您必须实现自己的 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]
}
}
})