Jersey - Obtain the contents of an OutputStream in an Interceptor before calling context.proceed()

♀尐吖头ヾ 提交于 2019-12-22 14:55:14

问题


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:

  1. Hold onto the old OutputStream from the context, with context.getOutputStream()
  2. Create a ByteArrayOutputStream and set that as the OutputStream on the context, with context.setOutputStream(baos)
  3. Call context.proceed(). What this does is have the MessageBodyWriter write to the ByteArrayOutputStream.
  4. Get the byte[] from the ByteArrayOutputStream with baos.toByteArray()
  5. Checksum the byte[] and set the header
  6. Write the byte[] to the old OutputStream.
  7. Finally set the OutputStream on the context to the old OutputStream.

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!