scala akka akka-http

akka HttpResponse leyó body como String scala



akka http client json (6)

Aquí está mi ejemplo de trabajo,

import akka.actor.ActorSystem import akka.http.scaladsl.Http import akka.http.scaladsl.model._ import akka.stream.ActorMaterializer import akka.util.ByteString import scala.concurrent.Future import scala.util.{ Failure, Success } def getDataAkkaHTTP:Unit = { implicit val system = ActorSystem() implicit val materializer = ActorMaterializer() // needed for the future flatMap/onComplete in the end implicit val executionContext = system.dispatcher val url = "http://localhost:8080/" val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = url)) responseFuture.onComplete { case Success(res) => { val HttpResponse(statusCodes, headers, entity, _) = res println(entity) entity.dataBytes.runFold(ByteString(""))(_ ++ _).foreach (body => println(body.utf8String)) system.terminate() } case Failure(_) => sys.error("something wrong") } }

Así que tengo una función con esta firma (akka.http.model.HttpResponse):

def apply(query: Seq[(String, String)], accept: String): HttpResponse

Simplemente obtengo un valor en una prueba como:

val resp = TagAPI(Seq.empty[(String, String)], api.acceptHeader)

Quiero comprobar su cuerpo en una prueba algo así como:

resp.entity.asString == "tags"

Mi pregunta es ¿cómo puedo obtener el cuerpo de respuesta como una cadena?


Como Akka Http se basa en transmisiones, la entidad también está transmitiendo. Si realmente necesita toda la cadena a la vez, puede convertir la solicitud entrante en una Strict :

Esto se hace usando la toStrict(timeout: FiniteDuration)(mat: Materializer) para recopilar la solicitud en una entidad estricta dentro de un límite de tiempo dado (esto es importante ya que no quiere "tratar de recopilar la entidad para siempre" en caso de que la solicitud entrante nunca termine):

import akka.stream.ActorFlowMaterializer import akka.actor.ActorSystem implicit val system = ActorSystem("Sys") // your actor system, only 1 per app implicit val materializer = ActorFlowMaterializer() // you must provide a materializer import system.dispatcher import scala.concurrent.duration._ val timeout = 300.millis val bs: Future[ByteString] = entity.toStrict(timeout).map { _.data } val s: Future[String] = bs.map(_.utf8String) // if you indeed need a `String`


Desafortunadamente en mi caso, Unmarshal to String no funcionó correctamente quejándose en: Unsupported Content-Type, supported: application/json . Esa sería una solución más elegante, pero tenía que usar otra forma. En mi prueba usé el Futuro extraído de la entidad de la respuesta y Esperar (de scala.concurrent) para obtener el resultado del Futuro:

Put("/post/item", requestEntity) ~> route ~> check { val responseContent: Future[Option[String]] = response.entity.dataBytes.map(_.utf8String).runWith(Sink.lastOption) val content: Option[String] = Await.result(responseContent, 10.seconds) content.get should be(errorMessage) response.status should be(StatusCodes.InternalServerError) }

Si necesita ir a través de todas las líneas en una respuesta, puede usar runForeach of Source:

response.entity.dataBytes.map(_.utf8String).runForeach(data => println(data))


También puedes probar este también.

responseObject.entity.dataBytes.runFold(ByteString(""))(_ ++ _).map(_.utf8String) map println


import akka.http.scaladsl.unmarshalling.Unmarshal implicit val system = ActorSystem("System") implicit val materializer = ActorFlowMaterializer() val responseAsString: Future[String] = Unmarshal(entity).to[String]


Unmarshaller.stringUnmarshaller(someHttpEntity)

Funciona como un amuleto, también se necesita materializador implícito.