2014-12-23 14 views
6

szukam sposobu, aby wydrukować treść odpowiedzi w ramach Play, mam kod jak poniżej:Play/Rejestrowanie/Drukuj Response nadwozia/przejechany wyliczający/buffer ciało

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

Obecnie Kod zakomentowanych nie działa jak chciałem, to znaczy, że to wydrukować:

Response: 
id=1 
body=pla[email protected] 

tak, muszę znaleźć sposób, aby uzyskać ciąg z Enumerator [Array [Byte]]. Starałem się zrozumieć ideę Enumerator czytając to: http://mandubian.com/2012/08/27/understanding-play2-iteratees-for-normal-humans/

więc ... jeśli dobrze rozumiem poprawnie:

  1. I nie powinno sucha-up wyliczający w procesie przekształcania go na ciąg. W przeciwnym razie klient nie otrzyma nic.

  2. Załóżmy, że wymyślę, jak zaimplementować mechanizm T/filtra. Ale czy ... czy nie zniszczyłoby to celu struktury Play jako nieblokującej frameworki strumieniowej (ponieważ budowałbym całą tablicę bajtów w pamięci, przed wywołaniem doString na nim, a na koniec logowaniem się)?

Jaki jest właściwy sposób rejestrowania odpowiedzi?

Dzięki z góry, Raka

+0

Powinieneś zarejestrować odpowiedź w taki sposób, aby przesyłała strumieniowo. Na przykład. 'System.out' jest' OutputStream', możesz logować się tam w sposób streamingu (chociaż możesz skończyć z dwiema przeplecionymi odpowiedziami). Jeśli logujesz się do bazy danych, możesz przejść do tego strumienia. I tak dalej. – lmm

+0

Myślę, że znalazłem odpowiedź, tutaj: http://stackoverflow.com/questions/17752151/scala-play-2-1-accessing-request-and-response-bodies-in-a-filter –

Odpowiedz

3

Ten kod działa:

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

ja nauczyłem go częściowo stąd (jak zdobyć tablicę bajtów Spośród wyliczający): Scala Play 2.1: Accessing request and response bodies in a filter.

Używam Play 2.3.7, podczas gdy łącze, które podałem, używa 2.1 (i nadal używa PlainResult, którego już nie ma w 2.3).

1

Użyłem powyższej odpowiedzi jako punktu wyjścia, ale zauważyłem, że zarejestruje ona tylko odpowiedzi, jeśli ciało jest obecne. Musimy dostosować go do tego:

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

Ponieważ wydaje mi się, jeśli nie zalogowaniu wewnątrz result.body &> Enumeratee.map (jak zasugerowano w https://stackoverflow.com/a/27630208/1781549) i ciało wynik jest przedstawiony w więcej niż jednym fragmencie, a następnie każda porcja będzie zalogowany niezależnie. Prawdopodobnie tego nie chcesz.

chciałbym wdrożyć go tak:

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

Ale ostrzegamy: cała idea tego jest spożywać wszystkie dane z result.body Enumerator przed zalogowaniem (i powrót nowego Enumerator). Tak więc, jeśli odpowiedź jest duża, lub polegasz na przesyłaniu strumieniowym, prawdopodobnie jest to również rzecz, której nie chcesz.