J'ai donc une fonction avec cette signature (akka.http.model.HttpResponse):
def apply(query: Seq[(String, String)], accept: String): HttpResponse
Je reçois 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 du type:
resp.entity.asString == "tags"
Ma question est la suivante: comment obtenir le corps de la réponse sous forme de chaîne?
import akka.http.scaladsl.unmarshalling.Unmarshal
implicit val system = ActorSystem("System")
implicit val materializer = ActorFlowMaterializer()
val responseAsString: Future[String] = Unmarshal(entity).to[String]
Étant donné qu'Akka Http est basé sur des flux, l'entité diffuse également. Si vous avez vraiment besoin de la chaîne entière en même temps, vous pouvez convertir la requête entrante en une requête Strict
:
Pour ce faire, utilisez l’API toStrict(timeout: FiniteDuration)(mat: Materializer)
pour collecter la demande dans une entité stricte dans un délai donné (c’est important car vous ne voulez pas "essayer de collecter l’entité pour toujours" au cas où la demande entrante ne se termine 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 aussi essayer celui-ci aussi.
responseObject.entity.dataBytes.runFold(ByteString(""))(_ ++ _).map(_.utf8String) map println
Unmarshaller.stringUnmarshaller(someHttpEntity)
fonctionne comme un charme, un matérialisant implicite est également nécessaire
Malheureusement, dans mon cas, Unmarshal
à String ne fonctionnait pas correctement, 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 de Future:
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")
}
}