Ich habe also eine Funktion mit dieser Signatur (akka.http.model.HttpResponse):
def apply(query: Seq[(String, String)], accept: String): HttpResponse
Ich bekomme einfach einen Wert in einem Test wie:
val resp = TagAPI(Seq.empty[(String, String)], api.acceptHeader)
Ich möchte seinen Körper in einem Test untersuchen, etwa:
resp.entity.asString == "tags"
Meine Frage ist, wie ich den Antworttext als String erhalten kann.
import akka.http.scaladsl.unmarshalling.Unmarshal
implicit val system = ActorSystem("System")
implicit val materializer = ActorFlowMaterializer()
val responseAsString: Future[String] = Unmarshal(entity).to[String]
Da Akka Http auf Streams basiert, wird auch die Entität gestreamt. Wenn Sie wirklich die gesamte Zeichenfolge auf einmal benötigen, können Sie die eingehende Anforderung in eine Strict
-Anweisung konvertieren:
Dazu verwenden Sie die toStrict(timeout: FiniteDuration)(mat: Materializer)
-API, um die Anforderung innerhalb einer bestimmten Zeit in einer strengen Entität zu sammeln. :
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`
Sie können dieses auch probieren.
responseObject.entity.dataBytes.runFold(ByteString(""))(_ ++ _).map(_.utf8String) map println
Hier ist eine einfache Anweisung, die string
aus dem Hauptteil der Anfrage extrahiert
def withString(): Directive1[String] = {
extractStrictEntity(3.seconds).flatMap { entity =>
provide(entity.data.utf8String)
}
}
Unmarshaller.stringUnmarshaller(someHttpEntity)
funktioniert wie ein Zauber, implizite Materializer benötigt
Leider funktionierte Unmarshal
to String nicht richtig und beschwerte sich über: Unsupported Content-Type, supported: application/json
. Das wäre eine elegantere Lösung, aber ich musste einen anderen Weg wählen. In meinem Test habe ich Future aus Entity der Antwort und Await (von scala.concurrent) extrahiert, um das Ergebnis aus der Future zu erhalten:
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)
}
Wenn Sie alle Zeilen in einer Antwort durchgehen müssen, können Sie runForeach
von Source verwenden:
response.entity.dataBytes.map(_.utf8String).runForeach(data => println(data))
Hier ist mein Arbeitsbeispiel,
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")
}
}