I want to get the Response body during Post HandlerInterceptor but it comes up empty.
spring-boot.version: 2.7.4
CustomPostHandlerInterceptor.java
@Component
@Order(1)
public class CustomPostHandlerInterceptor implements HandlerInterceptor {
@Override
public final boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
@Override
public final void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
ContentCachingResponseWrapper resp = new ContentCachingResponseWrapper(response);
byte[] responseBody = resp.getContentAsByteArray();
String res = new String(responseBody, StandardCharsets.UTF_8);
}
@Override
public final void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) throws Exception {
}
}
Controller class
@RestController
@RequestMapping("/exampleRest")
public class ExampleRest {
@RequestMapping("/getUsername")
@GetMapping
public String getUsername() {
return "michael";
}
}
I want to get response body from HttpServletResponse. I tried different methods but it comes up empty. How can I fix?
CodePudding user response:
AFAIK the response can only be read once so you need to copy it.
You need a filter as follows:
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
@Component
public class LogFilter extends HttpFilter {
private FilterConfig filterConfigObj;
public void init(FilterConfig config) throws ServletException {
this.filterConfigObj = config;
}
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException {
HttpServletResponseCopier responseCopier = new HttpServletResponseCopier((HttpServletResponse) response);
try {
chain.doFilter(request, responseCopier);
responseCopier.flushBuffer();
} finally {
byte[] copy = responseCopier.getCopy();
System.out.println(new String(copy, response.getCharacterEncoding()));
}
}
}
And these dependent classes to read and preserve the response.
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class HttpServletResponseCopier extends HttpServletResponseWrapper {
private ServletOutputStream outputStream;
private PrintWriter writer;
private ServletOutputStreamCopier copier;
public HttpServletResponseCopier(HttpServletResponse response) throws IOException {
super(response);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (writer != null) {
throw new IllegalStateException("getWriter() has already been called on this response.");
}
if (outputStream == null) {
outputStream = getResponse().getOutputStream();
copier = new ServletOutputStreamCopier(outputStream);
}
return copier;
}
@Override
public PrintWriter getWriter() throws IOException {
if (outputStream != null) {
throw new IllegalStateException("getOutputStream() has already been called on this response.");
}
if (writer == null) {
copier = new ServletOutputStreamCopier(getResponse().getOutputStream());
writer = new PrintWriter(new OutputStreamWriter(copier, getResponse().getCharacterEncoding()), true);
}
return writer;
}
@Override
public void flushBuffer() throws IOException {
if (writer != null) {
writer.flush();
} else if (outputStream != null) {
copier.flush();
}
}
public byte[] getCopy() {
if (copier != null) {
return copier.getCopy();
} else {
return new byte[0];
}
}
}
and
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
public class ServletOutputStreamCopier extends ServletOutputStream {
private OutputStream outputStream;
private ByteArrayOutputStream copy;
public ServletOutputStreamCopier(OutputStream outputStream) {
this.outputStream = outputStream;
this.copy = new ByteArrayOutputStream(1024);
}
@Override
public void write(int b) throws IOException {
outputStream.write(b);
copy.write(b);
}
public byte[] getCopy() {
return copy.toByteArray();
}
@Override
public boolean isReady() {
// TODO Auto-generated method stub
return true;
}
@Override
public void setWriteListener(WriteListener listener) {
// TODO Auto-generated method stub
}
}
CodePudding user response:
You will have to cache the response in the first place, you can extend the OncePerRequestFilter from spring and then cache your response, the following code registers a filter that creates a ContentCachingWrapper for your request and response, using this you should be able to get the response using the code that you have mentioned.
@Component
public class CachingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(new ContentCachingRequestWrapper(request), ContentCachingResponseWrapper(response));
}