how modify the response body with java filter?

14,157

Thanks to the help of JBNizet, I found that the solution was to add the Content Lenght:

String newContent = "{ \"name\" : \"************r\" }";
response.setContentLength(newContent .length());
response.getWriter().write(newContent);
Share:
14,157
JaimeRG
Author by

JaimeRG

Updated on July 20, 2022

Comments

  • JaimeRG
    JaimeRG almost 2 years

    I want to perform some filter logic to HTTP responses (which are in json format).

    I had successfully change the response body, but when the (string) size of the body changes: I am getting missing the last characters.

    To make it simpler, I had created a simple Spring Boot application, with only Web dependency for my rest controller.

    My Rest Controller

    @RestController
    @RequestMapping("/home/")
    public class RestControllerHome {
    
    @GetMapping (produces=MediaType.APPLICATION_JSON_VALUE)
    public String home() {
            return "{ \"name\" : \"Peter\" }";
      }
    }
    

    My Filter

    @Component
    public class MyFilter implements Filter {
    
    @Override
    public void destroy() { }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
    
        HtmlResponseWrapper capturingResponseWrapper = new HtmlResponseWrapper((HttpServletResponse) response);
        filterChain.doFilter(request, capturingResponseWrapper);        
        if (response.getContentType() != null && response.getContentType().contains("application/json")) {
            String content = capturingResponseWrapper.getCaptureAsString();
    
            // This code works fine
            //response.getWriter().write(content.toUpperCase());
    
            // This code doesn't works because the content size is changed
            response.getWriter().write("{ \"name\" : \"************r\" }");
    
        }
    }
    
    @Override
    public void init(FilterConfig arg0) throws ServletException {  }    
    }
    

    HttpServletResponseWrapper // capture the response before it is written

    public class HtmlResponseWrapper extends HttpServletResponseWrapper {
    
    private final ByteArrayOutputStream capture;
    private ServletOutputStream output;
    private PrintWriter writer;
    
    public HtmlResponseWrapper(HttpServletResponse response) {
        super(response);
        capture = new ByteArrayOutputStream(response.getBufferSize());
    }
    
    @Override
    public ServletOutputStream getOutputStream() {
        if (writer != null) {
            throw new IllegalStateException("getWriter() has already been called on this response.");
        }
    
        if (output == null) {
            // inner class - lets the wrapper manipulate the response 
            output = new ServletOutputStream() {
                @Override
                public void write(int b) throws IOException {
                    capture.write(b);
                }
    
                @Override
                public void flush() throws IOException {
                    capture.flush();
                }
    
                @Override
                public void close() throws IOException {
                    capture.close();
                }
    
                @Override
                public boolean isReady() {
                    return false;
                }
    
                @Override
                public void setWriteListener(WriteListener arg0) {
                }
            };
        }
    
        return output;
    }
    
    @Override
    public PrintWriter getWriter() throws IOException {
        if (output != null) {
            throw new IllegalStateException("getOutputStream() has already been called on this response.");
        }
    
        if (writer == null) {
            writer = new PrintWriter(new OutputStreamWriter(capture,
                    getCharacterEncoding()));
        }
    
        return writer;
    }
    
    @Override
    public void flushBuffer() throws IOException {
        super.flushBuffer();
    
        if (writer != null) {
            writer.flush();
        } else if (output != null) {
            output.flush();
        }
    }
    
    public byte[] getCaptureAsBytes() throws IOException {
        if (writer != null) {
            writer.close();
        } else if (output != null) {
            output.close();
        }
    
        return capture.toByteArray();
    }
    
    public String getCaptureAsString() throws IOException {
        return new String(getCaptureAsBytes(), getCharacterEncoding());
    }
    
    }
    

    In my doFilter method, the following code ...

    // This code works fine
    response.getWriter().write(content.toUpperCase());
    
    // This code doesn't works because the content size is changed
    //response.getWriter().write("{ \"name\" : \"************r\" }");
    

    ... gives my the following output : {"NAME": "PETER"} Which tell me, that the code is working properly.

    But, in reality I want to change the body content ...

    // This code works fine
    //response.getWriter().write(content.toUpperCase());
    
    // This code doesn't works because the content size is changed
    response.getWriter().write("{ \"name\" : \"************r\" }");
    

    ... and the previous code, is giving me an incomplete text body as output: **{ "name" : "**********

    What am I doing wrong? My app have a bigger json body, and a little more complex logic in the filter. But, if I dont get this working I am not being able to make the rest of my code work. Please, help.

    I took the Filter and HttpServletResponseWrapper from https://www.leveluplunch.com/java/tutorials/034-modify-html-response-using-filter/

  • pkgajulapalli
    pkgajulapalli over 4 years
    I'm getting getWriter() has already been called for this response error with this solution. Was able to work with medium.com/@sportans300/…?