Play / Record / Fingerprint Response Body / Launch Enumerator / Body Buffer

I am looking for a way to print the response body in the Play platform, I have this code:

object AccessLoggingAction extends ActionBuilder[Request] { def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = { Logger.info(s"""Request: id=${request.id} method=${request.method} uri=${request.uri} remote-address=${request.remoteAddress} body=${request.body} """) val ret = block(request) /* ret.map {result => Logger.info(s"""Response: id=${request.id} body=${result.body} """) } */ //TODO: find out how to print result.body (be careful not to consume the enumerator) ret } } 

Currently, the code with comments does not work the way I wanted, I mean, it will print:

 Response: id=1 body=play.api.libs.iteratee.Enumerator$$anon$18@39e6c1a2 

So, I need to find a way to get a string from Enumerator [Array [Byte]]. I tried to understand the concept of Enumerator by reading this: http://mandubian.com/2012/08/27/understanding-play2-iteratees-for-normal-humans/

So ... if I understand correctly:

  • I should not clean out the counter in the process of converting it to String. Otherwise, the client will not receive anything.

  • Suppose I figure out how to implement the T / filter mechanism. But then ... would it not surpass the goal of the Play platform as a non-blocking streaming infrastructure (because I would build up a full array of bytes in memory before calling toString on it and finally registering it)?

So what is the right way to register a response?

Thanks in advance, Raka

+6
source share
3 answers

This code works:

 object AccessLoggingAction extends ActionBuilder[Request] { def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = { val start = System.currentTimeMillis Logger.info(s"""Request: id=${request.id} method=${request.method} uri=${request.uri} remote-address=${request.remoteAddress} body=${request.body} """) val resultFut = block(request) resultFut.map {result => val time = System.currentTimeMillis - start Result(result.header, result.body &> Enumeratee.map(arrOfBytes => { val body = new String(arrOfBytes.map(_.toChar)) Logger.info(s"""Response: id=${request.id} method=${request.method} uri=${request.uri} delay=${time}ms status=${result.header.status} body=${body}""") arrOfBytes }), result.connection) } } } 

I partially learned about it here (how to get a byte array from an enumerator): Scala Play 2.1: Access to the request and response bodies in the filter ,

I use Play 2.3.7, while the link I gave uses 2.1 (and still uses PlainResult, which no longer exists in version 2.3).

+3
source

It seems to me that if you register inside result.body &> Enumeratee.map (as suggested in fooobar.com/questions/980059 / ... ), and the body of the result is presented in more than one piece, then each block will be registered independently. You probably don't want this.

I would execute it as follows:

 val ret = block(request).flatMap { result => val consume = Iteratee.consume[Array[Byte]]() val bodyF = Iteratee.flatten(result.body(consume)).run bodyF.map { bodyBytes: Array[Byte] => // // Log the body // result.copy(body = Enumerator(bodyBytes)) } } 

But be careful: the whole idea of ​​this is to use all the data from result.body Enumerator before logging (and return a new Enumerator). So, if the answer is big, or you rely on streaming, then this is probably also what you don't want.

+2
source

I used the above answer as a starting point, but noticed that it will only log responses if the body is present. We adapted it to this:

 var responseBody = None:Option[String] val captureBody = Enumeratee.map[Array[Byte]](arrOfBytes => { val body = new String(arrOfBytes.map(_.toChar)) responseBody = Some(body) arrOfBytes }) val withLogging = (result.body &> captureBody).onDoneEnumerating({ logger.debug(.. create message here ..) }) result.copy(body=withLogging) 
+1
source

Source: https://habr.com/ru/post/980059/


All Articles