Ответ 1
import akka.http.scaladsl.unmarshalling.Unmarshal
implicit val system = ActorSystem("System")
implicit val materializer = ActorFlowMaterializer()
val responseAsString: Future[String] = Unmarshal(entity).to[String]
Итак, у меня есть функция с этой сигнатурой (akka.http.model.HttpResponse):
def apply(query: Seq[(String, String)], accept: String): HttpResponse
Я просто получаю значение в тесте, например:
val resp = TagAPI(Seq.empty[(String, String)], api.acceptHeader)
Я хочу проверить его тело в тесте что-то вроде:
resp.entity.asString == "tags"
Мой вопрос в том, как я могу получить тело ответа как строку?
import akka.http.scaladsl.unmarshalling.Unmarshal
implicit val system = ActorSystem("System")
implicit val materializer = ActorFlowMaterializer()
val responseAsString: Future[String] = Unmarshal(entity).to[String]
Так как Akka Http основан на потоках, объект также работает. Если вам действительно нужна вся строка сразу, вы можете преобразовать входящий запрос в Strict
one:
Это делается с помощью API toStrict(timeout: FiniteDuration)(mat: Materializer)
для сбора запроса в строгий объект в течение заданного срока (это важно, поскольку вы не хотите "пытаться собирать объект навсегда" в случае, если входящий запрос на самом деле никогда не заканчивается):
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`
Вы также можете попробовать это.
responseObject.entity.dataBytes.runFold(ByteString(""))(_ ++ _).map(_.utf8String) map println
К сожалению, в моем случае Unmarshal
для String не работал должным образом, жалуясь на: Unsupported Content-Type, supported: application/json
. Это было бы более элегантное решение, но мне пришлось использовать другой способ. В моем тесте я использовал Future, извлеченный из объекта ответа и Await (из scala.concurrent), чтобы получить результат из Будущего:
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)
}
Если вам нужно пройти все строки в ответе, вы можете использовать runForeach
для источника:
response.entity.dataBytes.map(_.utf8String).runForeach(data => println(data))
Вот мой рабочий пример,
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)
работает как шарм, нужен неявный материализатор
Вот простая директива, которая извлекает string
из тела запроса
def withString(): Directive1[String] = {
extractStrictEntity(3.seconds).flatMap { entity =>
provide(entity.data.utf8String)
}
}