我正在寻找一种在Play框架中打印响应体的方法,我有这样的代码:
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
}
}
目前,已注释掉的代码无法正常工作,我的意思是,它会打印出来:
Response:
id=1
body=play.api.libs.iteratee.Enumerator$$anon$18@39e6c1a2
所以,我需要找到一种从Enumerator [Array [Byte]]中获取String的方法。我试着通过阅读:http://mandubian.com/2012/08/27/understanding-play2-iteratees-for-normal-humans/
来掌握Enumerator的概念所以...,如果我理解正确的话:
在将枚举器转换为String的过程中,我不应该干掉它。否则,客户端将不会收到任何信息。
假设我弄清楚如何实现T /过滤器机制。但那么......它不会将Play框架的目的打败为非阻塞流式传输框架(因为我将在内存中构建完整的字节数组,然后在其上调用toString,最后记录它)
那么,记录响应的正确方法是什么?
提前致谢, 拉嘎
答案 0 :(得分:3)
此代码有效:
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)
}
}
}
我从这里部分学习了它(关于如何从枚举器中获取字节数组):Scala Play 2.1: Accessing request and response bodies in a filter。
我使用Play 2.3.7,而我给出的链接使用2.1(仍使用PlainResult,2.3中不再存在)。
答案 1 :(得分:2)
在我看来,如果您在result.body &> Enumeratee.map
内进行记录(如https://stackoverflow.com/a/27630208/1781549中所示)并且结果正文显示在多个块中,则每个块将被独立记录。你可能不想要这个。
我是这样实现的:
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))
}
}
但要注意:这样做的全部意思是在记录之前使用result.body
枚举器中的所有数据(并返回新的枚举器)。所以,如果回复很大,或者你依赖流媒体,那么它可能也是你不想要的。
答案 2 :(得分:1)
我使用上面的答案作为起点,但注意到它只会在身体存在时记录响应。我们已将其改编为:
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)