akka HttpResponse lire le corps comme chaîne scala
J'ai Donc une fonction avec cette signature (akka.http.modèle.HttpResponse):
def apply(query: Seq[(String, String)], accept: String): HttpResponse
J'obtiens simplement une valeur dans un test comme:
val resp = TagAPI(Seq.empty[(String, String)], api.acceptHeader)
Je veux vérifier son corps dans un test quelque chose comme:
resp.entity.asString == "tags"
Ma question Est de savoir comment obtenir le corps de réponse en tant que chaîne?
6 réponses
import akka.http.scaladsl.unmarshalling.Unmarshal
implicit val system = ActorSystem("System")
implicit val materializer = ActorFlowMaterializer()
val responseAsString: Future[String] = Unmarshal(entity).to[String]
Comme Akka Http est basé sur des flux, L'entité est également en streaming. Si vous avez vraiment besoin de la chaîne entière à la fois, vous pouvez convertir la requête entrante en Strict
Un:
Ceci est fait en utilisant l'API toStrict(timeout: FiniteDuration)(mat: Materializer)
pour collecter la requête dans une entité stricte dans un délai donné (ceci est important car vous ne voulez pas "essayer de collecter l'entité pour toujours" au cas où la requête entrante ne finirait jamais):
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`
Vous pouvez également essayer celui-ci aussi.
responseObject.entity.dataBytes.runFold(ByteString(""))(_ ++ _).map(_.utf8String) map println
Malheureusement dans mon cas, Unmarshal
to String n'a pas fonctionné correctement en se plaignant de: Unsupported Content-Type, supported: application/json
. Ce serait une solution plus élégante, mais je devais utiliser un autre moyen. Dans mon test, J'ai utilisé Future extrait de l'entité de la réponse et Await (de scala.concurrent) pour obtenir le résultat du futur:
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 vous devez parcourir toutes les lignes d'une réponse, vous pouvez utiliser runForeach
de Source:
response.entity.dataBytes.map(_.utf8String).runForeach(data => println(data))
Voici mon exemple de travail,
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")
}
}
Unmarshaller.stringUnmarshaller(someHttpEntity)
Fonctionne comme un charme, le matérialiseur implicite est également nécessaire