Spring MVC - Get HttpServletResponse body
It's been hard on searching deeply into it but found that ResponseBodyAdvice
could be suitable for my purposes. So looking for some example on StackOverflow found this guy which had quite same issue having to manipulate the Object body
.
That's my final working solution in order to implement what I wrote here
@ControllerAdvice
public class CSRFHandler implements ResponseBodyAdvice<Object> {
@Value("${security.csrf.enabled}")
private String csrfEnabled;
@Value("${security.csrf.headerName}")
private String csrfHeaderName;
@Value("${security.csrf.salt}")
private String salt;
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
if (new Boolean(csrfEnabled).booleanValue()) {
String csrfValue = SecureUtil.buildCsrfValue(salt, StringUtil.toJson(body));
response.getHeaders().add(csrfHeaderName, csrfValue);
}
return body;
}
}
Logging response body (HTML) from HttpServletResponse using Spring MVC HandlerInterceptorAdapter
This would be better done using a Servlet Filter
rather than a Spring HandlerInterceptor
, for the reason that a Filter
is allowed to substitute the request and/or response objects, and you could use this mechanism to substitute the response with a wrapper which logs the response output.
This would involve writing a subclass of HttpServletResponseWrapper
, overriding getOutputStream
(and possibly also getWriter()
). These methods would return OutputStream
/PrintWriter
implementations that siphon off the response stream into a log, in addition to sending to its original destination. An easy way to do this is using TeeOutputStream
from Apache Commons IO, but it's not hard to implement yourself.
Here's an example of the sort of thing you could do, making use of Spring's GenericFilterBean
and DelegatingServletResponseStream
, as well as TeeOutputStream
, to make things easier:
public class ResponseLoggingFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse responseWrapper = loggingResponseWrapper((HttpServletResponse) response);
filterChain.doFilter(request, responseWrapper);
}
private HttpServletResponse loggingResponseWrapper(HttpServletResponse response) {
return new HttpServletResponseWrapper(response) {
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new DelegatingServletOutputStream(
new TeeOutputStream(super.getOutputStream(), loggingOutputStream())
);
}
};
}
private OutputStream loggingOutputStream() {
return System.out;
}
}
This logs everything to STDOUT. If you want to log to a file, it'll get a big more complex, what with making sure the streams get closed and so on, but the principle remains the same.
Using HttpServletResponse object in spring MVC handler method
out.println()
is used in jsp file. So you can edit your home.jsp as follows
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
<link href="<c:url value='/resources/css/style.css' />"
rel="stylesheet">
<script src="<c:url value='/resources/js/sample.css' /> "></script>
</head>
<body>
This is home view
<%
out.println("<h1> this is my response block</h1>");
%>
</body>
</html>
If you want to add something for home.jsp via the controller class, you can change your controller class as follows.
package springmvcsearch;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class SearchController {
@RequestMapping("/home")
public String home(HttpServletRequest req,HttpServletResponse res) throws IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
String message = "this is my response block";
req.setAttribute("message", message);
return "home";
}
}
Then change your jsp file as follows.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
<link href="<c:url value='/resources/css/style.css' />"
rel="stylesheet">
<script src="<c:url value='/resources/js/sample.css' /> "></script>
</head>
<body>
This is home view
<h1>${message}</h1>
</body>
</html>
For further explanation, go through this link how to message from servlet and display in jsp
Why does HttpServletResponse not have a simple String of the response body?
HttpServletRequest
/Response
classes are designed to stream the data directly from/to the browser. They are there to handle the I/O communication over the network, but they do not need to store the whole request/response body.
This allows servlets to directly stream data, and by the time you would want to log the response, the data has already been sent over the network (well, except with async processing but that makes things even more complicated for your purposes). A default implementation wouldn’t know that it had to keep a copy of the data for logging purposes.
This is why you need to implement a Filter
that would wrap the request/response in order to keep and log the body, as you have pointed in your link. In general you will probably want to customize this behavior in order to select only specific requests, limit the size, define how to log it etc. so a default implementation is not provided, but you have a lot of solutions.
Get responseBody from HttpServletResponse Java
package com.biclinical.rmm.controller;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;
import com.biclinical.rmm.utility.ServiceUtils;
@Component
@Order(1)
@WebFilter(urlPatterns = { "/*" })
public class DownloadFileFilter implements Filter {
private static final Logger logger = Logger.getLogger(DownloadFileFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest requestToCache = new ContentCachingRequestWrapper((HttpServletRequest) request);
HttpServletResponse responseToCache = new ContentCachingResponseWrapper((HttpServletResponse) response);
try {
if (requestToCache.getRequestURL().toString().contains("/download")) {
chain.doFilter(request, responseToCache);
getResponseData(responseToCache);
} else {
chain.doFilter(request, response);
}
} catch (Exception e) {
logger.error("Exception in CustomFilter.doFilter(): ", e);
}
}
private static void getResponseData(HttpServletResponse response) throws Exception {
ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response,
ContentCachingResponseWrapper.class);
if (wrapper != null) {
byte[] buf = wrapper.getContentAsByteArray();
if (buf.length > 0) {
String payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
logger.debug("Response Richa :" + payload);
wrapper.copyBodyToResponse();
}
}
}
@Override
public void destroy() {
}
}
Related Topics
Rounded Swing Jbutton Using Java
How to Remove All Special Character in a String Except Dot and Comma
How to Exclude Classes from Test Coverage Using Java Annotations in Spring Boot
After Spring Boot 2.0 Migration: Jdbcurl Is Required With Driverclassname
How to Store a Large (10 Digits) Integer
Spring Boot JPA - Onetomany Relationship Causes Infinite Loop
Javafx Column in Tableview Auto Fit Size
Remove Keys from Anywhere in a Json String Without Deserialization
How to Truncate a Double to Only Two Decimal Places in Java
Java Error Message. Unexpected Type, Required Variable, Found Value
Hibernate Foreign Key Issue:Error Executing Ddl "Alter Table..."
Checking If a String Is Contained in an Enum Set in O(1)
How to Convert Xml to Java.Util.Map and Vice Versa
How to Mock System.Getproperty Using Mockito
How to Find Out My MySQL Url, Host, Port and Username
How to Pass Authentication Credentials in Vba
How to Unpackage and Repackage a War File
How to Subtract Number of Days from Current Date in Hql Query