Abhijit Sarkar
Abhijit Sarkar

Reputation: 24518

Play Framework: How to modify the response body without blocking?

I'm cutting my teeth on Play using the book Reactive Web Applications: Covers Play, Akka, and Reactive Streams. Chapter 4, among other things, teaches how to write a filter, but the code shown in the book doesn't compile, because in Play 2.4.x, Result.body used to be Enumerator[Array[Byte]] and in 2.5.x it is play.api.http.HttpEntity.

My version of the filter is as below:

class ScoreFilter @Inject()(implicit val mat: Materializer, ec: ExecutionContext) extends Filter {
  override def apply(nextFilter: (RequestHeader) => Future[Result])(rh: RequestHeader) =
    nextFilter(rh).map { result =>
      if (result.header.status == OK || result.header.status == NOT_ACCEPTABLE) {
        val correct = result.session(rh).get("correct").getOrElse(0)
        val wrong = result.session(rh).get("wrong").getOrElse(0)

        val score = s"\nYour current score is: $correct correct " +
          s"answers and $wrong wrong answers"

        val contentType = result.body.contentType
        val scoreByteString = ByteString(score.getBytes(UTF_8))
        val maybeNewBody = result.body.consumeData.map(_.concat(scoreByteString))

        import scala.concurrent.duration._
        val newBody = Await.result(maybeNewBody, 10 seconds)

        result.copy(body = Strict(newBody, contentType))
      } else {
        result
      }
    }
}

In the book:

// result.body returns a play.api.http.HttpEntity which doesn't have an andThen method
val newBody = result.body andThen Enumerator(score.getBytes(UTF_8))
result.copy(body = newBody)

As you can see, my version of the filter works but it blocks on the future. I'm wondering if there's a better way to do this without blocking?

P.S.: Before dismissing my question as duplicate, please be aware that I've read all of the following threads and they convert the response body into a string, which isn't what I want.

Scala play http filters: how to find the request body

Play framework filter that modifies json request and response

Play 2.4: intercept and modify response body

Upvotes: 3

Views: 2284

Answers (1)

handler
handler

Reputation: 1483

if you want to avoid Await.result, you can do:

nextFilter(rh).flatMap { result =>
  ...
  maybeNewBody map { newBody =>
    result.copy(body = Strict(newBody, contentType))
  }
} else Future.successful(result)

(note the change of map to flatMap)

Upvotes: 2

Related Questions