问题
Using an Interceptor in Jersey I can manipulate the Output, however, I also want to add a Header to the response which value is calculated from the result of the output.
@Sha256Sum
public class Sha256SumInterceptor implements WriterInterceptor {
public static final String SHA256_HASH_HEADER_NAME = "SHA256-SUM";
@Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
// Retrieve the OutputStream, read the contents and calculate the hashsum.
// Set the header value in context.
context.proceed();
}
}
However, the issue is that when I finally have read the entire stream, I'm unable to set the headers as when context.proceed is called and the contents written (and thus enabling me to do anything with it) I can no longer set the header.
My question in short: How do I capture the entire stream output as a byte[], calculate a result from the array of bytes and finally set the header in the response to the computed result? I do not want to deplete the output stream.
回答1:
If you've ever worked with an AOP framework or even CDI interceptors, you'll have worked with the concept of Around-Advice or Around-Invoke, respectively. You can perform operations before and after the invocation of the advised/intercepted method. context.proceed()
works the same way; it's the method invocation (or more precisely the MessageBodyWriter
doing it's writing). We can perform some operations before the MessageBodyWriter
does it's job, call proceed()
to let the writer do it's work, then we can do some more work.
With that said, here are the steps you can take:
- Hold onto the old
OutputStream
from thecontext
, withcontext.getOutputStream()
- Create a
ByteArrayOutputStream
and set that as theOutputStream
on the context, withcontext.setOutputStream(baos)
- Call
context.proceed()
. What this does is have theMessageBodyWriter
write to theByteArrayOutputStream
. - Get the
byte[]
from theByteArrayOutputStream
withbaos.toByteArray()
- Checksum the
byte[]
and set the header - Write the
byte[]
to the oldOutputStream
. - Finally set the
OutputStream
on thecontext
to the oldOutputStream
.
Here's the basic implementation (tested and works as expected)
@Provider
public class ChecksumInterceptor implements WriterInterceptor {
@Override
public void aroundWriteTo(WriterInterceptorContext context)
throws IOException, WebApplicationException {
OutputStream old = context.getOutputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
context.setOutputStream(buffer);
// let MessageBodyWriter do it's job
context.proceed();
// get bytes
byte[] entity = buffer.toByteArray();
String checksum = ChecksumUtil.createChecksum(entity);
context.getHeaders().putSingle("X-Checksum", checksum);
old.write(entity);
} finally {
context.setOutputStream(old);
}
}
}
来源:https://stackoverflow.com/questions/33518790/jersey-obtain-the-contents-of-an-outputstream-in-an-interceptor-before-calling