如何使用java过滤器修改响应主体?

时间:2018-08-13 16:06:25

标签: java json rest spring-boot filter

我想对HTTP响应(采用json格式)执行一些过滤逻辑。

我已经成功地更改了响应正文,但是当正文的(字符串)大小更改时:我越来越错过最后一个字符了。

为简化起见,我创建了一个简单的Spring Boot应用程序,其休息控制器仅具有Web依赖性。

我的休息控制器

@RestController
@RequestMapping("/home/")
public class RestControllerHome {

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

我的过滤器

@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     //在编写响应之前捕获响应

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());
}

}

在我的 doFilter 方法中,以下代码...

// 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\" }");

...给我以下输出: {“ NAME”:“ PETER”} 哪个告诉我,代码可以正常工作。

但是,实际上,我想更改身体内容...

// 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\" }");

...和前面的代码给我一个不完整的文本正文作为输出: ** {“ name”:“ **********

我在做什么错? 我的应用程序具有更大的json主体,并且过滤器中的逻辑更为复杂。但是,如果我无法正常工作,则无法使其余代码正常工作。请帮助。

我从https://www.leveluplunch.com/java/tutorials/034-modify-html-response-using-filter/那里获得了Filter和HttpServletResponseWrapper

2 个答案:

答案 0 :(得分:1)

借助JBNizet的帮助,我发现解决方案是添加内容长度:

String newContent = "{ \"name\" : \"************r\" }";
response.setContentLength(newContent .length());
response.getWriter().write(newContent);

答案 1 :(得分:0)

实际上,按照上面的回答,你会得到一个错误,说 getWriter() has already been called for this response

因此,您可以简单地修改最后一行,如下所示:

String newContent = "{ \"name\" : \"************r\" }";
response.setContentLength(newContent.length());
response.getOutputStream().write(newContent.getBytes());