如何使用circe或jackson或scala中的任何其他库为HttpResponse(Akka)编写编解码器

tez616oj  于 2022-11-23  发布在  Scala
关注(0)|答案(2)|浏览(142)

你好,我正在尝试将客户端的请求响应存储到文件中。我可以很容易地为HttpRequest完成此操作
但是,当我试图为HttpResponse编写Encoder decoder时,我无法理解如何在scala中为HttpResponse的实体编写它。
下面是HTTP请求编码器解码器的代码

val demo=HttpRequest(
  method= HttpMethods.GET,
uri="myUri",
headers=generateHeaders(Map.empty),
  entity="{\"customerReferenceIds\":[{\"customerId\":\"9600007934256702\",\"customerIdType\":\"CUSTOMER_ID\"}]}",
)

demo.asJson.spaces2

我能够轻松地为HttpRequest编写编码器解码器。

implicit val HttpRequestEncoder: Encoder[HttpRequest] = new Encoder[HttpRequest] {
  final def apply(x: HttpRequest): Json = Json.obj(
    ("method", Json.fromString(x.method.value)),
    ("Uri", Json.fromString(x.uri.toString())) ,
    ("headers", x.headers.map(y=>y.name->y.value).toMap.asJson),
    ("entity", Json.fromString(JsonUtil.toJson(x.entity)))
  )
}

implicit val HttpRequestDecoder: Decoder[HttpRequest] = new Decoder[HttpRequest] {
  final def apply(c: HCursor): Decoder.Result[HttpRequest] =
    for {
      method <- c.downField("method").as[String]
      url <- c.downField("Uri").as[String]
      header <- c.downField("headers").as[Map[String,String]]
      entity <- c.downField("entity").as[String]
    } yield {
      HttpRequest(
        method=HttpMethods.getForKeyCaseInsensitive(method).getOrElse(HttpMethods.GET),
          uri = url,
        headers=generateHeaders(header),
        entity= HttpEntity(ContentTypes.`application/json`,JsonUtil.toJson(entity))
      )
    }
}

我正在尝试为HttpResponse编写编码器解码器。

//HttpResponse
implicit val HttpResponseEncoder: Encoder[HttpResponse] = new Encoder[HttpResponse] {
  final def apply(x: HttpResponse): Json = {
    Json.obj(
      ("response", Json.fromString(JsonUtil.toJson(x.entity))),
      ("status", Json.fromInt(x.status.intValue()))
    )
  }
}

implicit val HttpResponseDecoder: Decoder[HttpResponse] = new Decoder[HttpResponse] {
  final def apply(c: HCursor): Decoder.Result[HttpResponse] =
    for {
      entity <- c.downField("response").as[String]
      status <- c.downField("status").as[Int]
    } yield {
      HttpResponse(
        status = StatusCode.int2StatusCode(status),
        entity = HttpEntity(ContentTypes.`application/json`, JsonUtil.toJson(entity))
      )
    }
}

其中,调试器

中的实体类似于

5anewei6

5anewei61#

HttpResponse的实体字段是一个流,所以你必须处理一些Encoder无法处理的效果-FutureStream
如果你的实体是一个json,那么我认为最简单的方法是使用entity.toStrict(timeout)。它返回Future[HttpEntity.Strict],其中有一个带普通String的data方法。
因此,您的逻辑可能如下所示:

val r: Future[EntityResponse] = 
 response
   .entity
   .toStrict(timeout)
   .map { e =>
     Json.obj(
       ("response", Json.fromString(JsonUtil.toJson(e.data))),
       ("status", Json.fromInt(response.status.intValue()))
     )
   }

在那里你可以找到关于http实体的详细信息以及如何使用它。

w6mmgewl

w6mmgewl2#

为人们寻找答案:我是这样做的:

val testconfig: Config                 = ConfigFactory.load()
      implicit val actorSystem2: ActorSystem = ActorSystem.create("loyalty-execution-api", testconfig)
    
      implicit val executionContext: ExecutionContext = actorSystem2.dispatcher
    
      implicit val HttpResponseEncoder: Encoder[HttpResponse] = new Encoder[HttpResponse] {
        import akka.http.scaladsl.unmarshalling
        final def apply(x: HttpResponse): Json = {
          val result = Unmarshal(x.entity).to[String].map { z =>
            Json.obj(
              ("response", Json.fromString(z)),
              ("status", Json.fromInt(x.status.intValue()))
            )
          }
          val r3     = Await.result(result, 20.seconds)
          r3
        }
      }
    
      implicit val HttpResponseDecoder: Decoder[HttpResponse] = new Decoder[HttpResponse] {
        final def apply(c: HCursor): Decoder.Result[HttpResponse] =
          for {
            entity <- c.downField("response").as[String]
            status <- c.downField("status").as[Int]
          } yield {
            HttpResponse(
              status = StatusCode.int2StatusCode(status),
              entity = HttpEntity(ContentTypes.`application/json`, JsonUtil.toJson(entity))
            )
          }
      }

此外,您还需要解组两次,一次用于解组实体,其中使用客户端响应,另一次用于编码器,因此在进行客户端调用时使用此方法:

val httpResponseFuture = client
          .singleRequest(
            clientRequest,
            settings = httpSettings,
            connectionContext = httpsCtx
          )
          .flatMap { response =>
            response.entity.toStrict(10.seconds).map { allBodyInMemory =>
              response.withEntity(allBodyInMemory)
            }

相关问题