我有完全相同的问题要解决。我使用了扩展Handler 的特征,就像在GZipSupport.scala 中所做的一样,并使用此answer 作为参考实现。
我构建了一个ServletOutputStreamCopier,其中包含原始OutputStream 的副本以及两个流的每个字节:
class ServletOutputStreamCopier(orig: ServletOutputStream) extends ServletOutputStream {
val copy: ByteArrayOutputStream = new ByteArrayOutputStream(1024)
override def write(b: Int): Unit = {
orig.write(b)
copy.write(b)
}
override def setWriteListener(writeListener: WriteListener): Unit = orig.setWriteListener(writeListener)
override def isReady: Boolean = orig.isReady
def getCopy: Array[Byte] = copy.toByteArray
}
然后是ResponseCopier,它是一个HttpServletResponseWrapper,前面定义了ServletOutputStreamCopier,并将copy暴露给外部:
class ResponseCopier(res: HttpServletResponse, sos: ServletOutputStreamCopier, w: PrintWriter) extends HttpServletResponseWrapper(res) {
override def getOutputStream: ServletOutputStream = new ServletOutputStreamCopier(sos)
override def getWriter: PrintWriter = w
override def setContentLength(i: Int) = {}
def getCopy: Array[Byte] = sos.getCopy
}
最后,handle 方法通过使用回调 ScalatraBase.onRenderedComplete 在 Scalatra 操作完成后负责添加标头。
trait SignedResponseSupport extends Handler {
self: ScalatraBase =>
abstract override def handle(req: HttpServletRequest, res: HttpServletResponse): Unit = {
withRequestResponse(req, res) {
val sosc = new ServletOutputStreamCopier(res.getOutputStream)
val w = new PrintWriter(sosc)
val wrapped = new ResponseCopier(response,sosc ,w)
ScalatraBase.onRenderedCompleted { _ =>
w.flush()
w.close()
val password = "secret-password"
val signature = signResponseBody(wrapped.getCopy, password)
wrapped.addHeader("X-Response-Signature", signature)
}
}
super.handle(req, wrapped)
}
def signResponseBody(body: Array[Byte], password: String): String = {
/*signing goes here*/
}
}