2014-12-23 14 views
6

Sto cercando un modo per stampare il corpo della risposta nel quadro Play, ho un codice come questo:Play/Logging/Stampa Response corpo/Investito enumeratore/tamponare il corpo

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

Attualmente la codice commentato-out non funziona come volevo, voglio dire, sarebbe stampare:

Response: 
id=1 
[email protected] 

Così, ho bisogno di trovare un modo per ottenere una stringa di Enumerator [Array [Byte]]. Ho cercato di afferrare il concetto di Enumerator leggendo questo: http://mandubian.com/2012/08/27/understanding-play2-iteratees-for-normal-humans/

Quindi ..., se ho capito bene:

  1. dovrei asciutto-up l'enumeratore nel processo di convertirlo accordare. Altrimenti, il cliente non riceverebbe nulla.

  2. Supponiamo di capire come implementare il meccanismo T/filtro. Ma poi ... non sarebbe in grado di sconfiggere lo scopo di Play framework come framework di streaming non bloccante (perché vorrei costruire l'intera gamma di byte in memoria, prima di chiamare a Stringa su di esso e infine registrarlo)?

Quindi, qual è il modo corretto di registrare la risposta?

Grazie in anticipo, Raka

+0

È necessario registrare la risposta in modo che sia in streaming. Per esempio. 'System.out' è un' OutputStream', puoi loggarti in streaming (anche se potresti avere due risposte interfogliate). Se si accede a un database, è possibile eseguire lo streaming. E così via. – lmm

+0

Penso di aver trovato la risposta, qui: http://stackoverflow.com/questions/17752151/scala-play-2-1-accession-request-and-response-bodies-in-a-filter –

risposta

3

questo codice funziona:

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

ho imparato in parte da qui (su come ottenere l'array di byte di enumeratore): Scala Play 2.1: Accessing request and response bodies in a filter.

Sto utilizzando Play 2.3.7 mentre il collegamento che ho fornito utilizza 2.1 (e utilizza ancora PlainResult, che non esiste più in 2.3).

1

Ho usato la risposta sopra come punto di partenza, ma ho notato che registrerà solo le risposte se è presente un corpo. Abbiamo adattato a questa:

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) 
2

come appare a me, se non la registrazione all'interno result.body &> Enumeratee.map (come suggerito in https://stackoverflow.com/a/27630208/1781549) e il corpo risultato viene presentato in più di un pezzo, quindi ogni blocco sarà registrato in modo indipendente. Probabilmente non vuoi questo.

mi piacerebbe implementare in questo modo:

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

Ma si ricordi: l'idea di questo è di consumare tutti i dati dal result.body Enumerator prima di accedere (e ritorno il nuovo Enumerator). Quindi, se la risposta è grande, o ti affidi allo streaming, allora probabilmente è anche la cosa che non vuoi.