akka HttpResponse 将 body 读取为 String scala
akka HttpResponse read body as String scala
所以我有一个带有这个签名的函数 (akka.http.model.HttpResponse):
def apply(query: Seq[(String, String)], accept: String): HttpResponse
我只是在测试中得到一个值,例如:
val resp = TagAPI(Seq.empty[(String, String)], api.acceptHeader)
我想在测试中检查它的 body:
resp.entity.asString == "tags"
我的问题是如何获得字符串形式的响应 body?
由于 Akka Http 是基于流的,因此该实体也是流式的。如果你真的需要一次整个字符串,你可以将传入的请求转换为 Strict
一个:
这是通过使用 toStrict(timeout: FiniteDuration)(mat: Materializer)
API 在给定的时间限制内将请求收集到一个严格的实体中来完成的(这很重要,因为您不想 "try to collect the entity forever"如果传入请求实际上永远不会结束):
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`
import akka.http.scaladsl.unmarshalling.Unmarshal
implicit val system = ActorSystem("System")
implicit val materializer = ActorFlowMaterializer()
val responseAsString: Future[String] = Unmarshal(entity).to[String]
不幸的是,在我的情况下,Unmarshal
到 String 没有正常工作,抱怨:Unsupported Content-Type, supported: application/json
。那将是更优雅的解决方案,但我不得不使用另一种方式。在我的测试中,我使用从响应实体中提取的 Future 和 Await(来自 scala.concurrent)从 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)
}
如果您需要遍历响应中的所有行,您可以使用 runForeach
of Source:
response.entity.dataBytes.map(_.utf8String).runForeach(data => println(data))
你也可以试试这个。
responseObject.entity.dataBytes.runFold(ByteString(""))(_ ++ _).map(_.utf8String) map println
这是我的工作示例,
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)
}
}
所以我有一个带有这个签名的函数 (akka.http.model.HttpResponse):
def apply(query: Seq[(String, String)], accept: String): HttpResponse
我只是在测试中得到一个值,例如:
val resp = TagAPI(Seq.empty[(String, String)], api.acceptHeader)
我想在测试中检查它的 body:
resp.entity.asString == "tags"
我的问题是如何获得字符串形式的响应 body?
由于 Akka Http 是基于流的,因此该实体也是流式的。如果你真的需要一次整个字符串,你可以将传入的请求转换为 Strict
一个:
这是通过使用 toStrict(timeout: FiniteDuration)(mat: Materializer)
API 在给定的时间限制内将请求收集到一个严格的实体中来完成的(这很重要,因为您不想 "try to collect the entity forever"如果传入请求实际上永远不会结束):
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`
import akka.http.scaladsl.unmarshalling.Unmarshal
implicit val system = ActorSystem("System")
implicit val materializer = ActorFlowMaterializer()
val responseAsString: Future[String] = Unmarshal(entity).to[String]
不幸的是,在我的情况下,Unmarshal
到 String 没有正常工作,抱怨:Unsupported Content-Type, supported: application/json
。那将是更优雅的解决方案,但我不得不使用另一种方式。在我的测试中,我使用从响应实体中提取的 Future 和 Await(来自 scala.concurrent)从 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)
}
如果您需要遍历响应中的所有行,您可以使用 runForeach
of Source:
response.entity.dataBytes.map(_.utf8String).runForeach(data => println(data))
你也可以试试这个。
responseObject.entity.dataBytes.runFold(ByteString(""))(_ ++ _).map(_.utf8String) map println
这是我的工作示例,
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)
}
}