7

我正在寻找一种在 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]] 中获取字符串的方法。我试图通过阅读以下内容来掌握枚举器的概念:http: //mandubian.com/2012/08/27/understanding-play2-iteratees-for-normal-humans/

所以...,如果我理解正确的话:

  1. 我不应该在将枚举数转换为字符串的过程中干涸。否则,客户端将一无所获。

  2. 假设我弄清楚了如何实现 T/filter 机制。但是……它不会破坏 Play 框架作为非阻塞流框架的目的(因为我会在内存中建立完整的字节数组,然后再调用它,最后记录它)?

那么,记录响应的正确方法是什么?

提前致谢, 拉卡

4

3 回答 3

4

此代码有效:

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 body in a filter

我使用的是 Play 2.3.7,而我提供的链接使用的是 2.1(并且仍然使用 PlainResult,它在 2.3 中不再存在)。

于 2014-12-24T01:09:30.560 回答
2

在我看来,如果您在内部进行日志记录(如https://stackoverflow.com/a/27630208/1781549result.body &> Enumeratee.map中所建议的那样)并且结果主体以多个块的形式呈现,那么每个块将被独立记录。你可能不想要这个。

我会这样实现它:

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在记录之前使用 Enumerator 中的所有数据(并返回新的 Enumerator)。因此,如果响应很大,或者您依赖流式传输,那么它可能也是您不想要的东西。

于 2016-04-27T10:51:29.670 回答
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)
于 2016-03-30T22:16:22.960 回答