web-dev-qa-db-fra.com

akka HttpResponse a lu corps comme String scala

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?

25
tg44
  import akka.http.scaladsl.unmarshalling.Unmarshal


  implicit val system = ActorSystem("System")  
  implicit val materializer = ActorFlowMaterializer() 

  val responseAsString: Future[String] = Unmarshal(entity).to[String]
22
user3548738

É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`
20

Vous pouvez aussi essayer celui-ci aussi. 

responseObject.entity.dataBytes.runFold(ByteString(""))(_ ++ _).map(_.utf8String) map println
7
Shashank Jain
Unmarshaller.stringUnmarshaller(someHttpEntity)

fonctionne comme un charme, un matérialisant implicite est également nécessaire

1
J. Nadberezny

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))
1
Shendor

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")
    }


  }
0
Charlie 木匠