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.