Add response header in HandlerInterceptorAdapter - spring

I am adding a header to the response inside HandlerInterceptorAdapter.
However it seems that the response header cannot be modified in the postHandle method.
public class CredentialInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
return true;
}
#Override
public void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView) {
String value = "...";
response.addHeader("header_name",value ); // doesn't work
}
}
How to add a header to the response ?
Popular solution is to use OncePerRequestFilter ( Set response header in Spring Boot ). Isn't there any other way ?

The problem with adding headers in the postHandle method is that the response may already be (partially) send. When that is the case you cannot add/change headers anymore. You need to set the headers before anything is sent to the client.
This you can do in the preHandle method or more generic a servlet filter before you call filterchain.doFilter. Doing it after the aforementioned call you might get the same issue that a response has already (partially) been sent.

Related

Spring - Headers getting cleared in ExceptionHandler

I have an exception handler in Spring. The goal is for this exception handler to just add some additional headers to the response.
If I set the status to 404 using response.setStatus, the headers get overwritten it seems like and I do not see the "something_random" header on the client side. It works fine if I omit the setStatus(404), but then the client gets a 200 with the header. Is there a way to ensure that the 404 response has the custom headers set?
Spring version: 4.3.25
What I've tried:
#ExceptionHandler(CustomNotFoundException.class)
public void handleFailure(Exception ex, HttpServletResponse response) {
response.setHeader("something_random", "bob");
reseponse.setStatus(404);
}
Also tried (not sure if different):
#ExceptionHandler(CustomNotFoundException.class)
public ResponseEntity<Object> handleFailure(Exception ex, HttpServletResponse response) {
// Initialize 'headers'
return new ResponseEntity<>(headers, HttpStatus.NOT_FOUND);
}
I have tried the following with Spring Boot 2.4.2 (Spring Framework 5.3.3) and the custom header is present in the response:
#ExceptionHandler(InvalidResponseException.class)
public ResponseEntity<String> handleOpenApiResponseValidationExceptions(InvalidResponseException ex, HttpServletResponse response) {
return ResponseEntity.status(INTERNAL_SERVER_ERROR)
.header("custom", "value")
.body(ex.getMessage());
}
I Know this is old question but here is one way that can fit into this.
#EnableWebMvc
#Configuration
public class ExceptionHandlingConfig {
#Autowired
private DispatcherServlet dispatcherServlet;
#PostConstruct
private void configureDispatcherServlet() {
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
}
}

How to get raw JSON message from request and HTTP status code from response

I am developing a Spring Boot application. We have a requirement to store raw JSON request and HTTP response code to store in database as part of processing the request.
We are able to intercept request in a class that extends RequestBodyAdviceAdapter. This class has implemented afterBodyRead method to get body of the request. Unfortunately there is no way to get the raw JSON request in this method.
Similarly we have another class that has implemented ResponseBodyAdvice to intercept response. In beforeBodyWrite method, response status code is not available.
You can write a simple servlet filter:
#Component
public class JsonFilter implements Filter {
#Override
public void doFilter
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
// Log JSON request
chain.doFilter(request, response);
// Log JSON response and HTTP Status code
}
// other methods
}
Read more about filters and Spring Boot here: https://www.baeldung.com/spring-boot-add-filter

Passin Parameters from Filter to Business Service in SpringBoot

I have 3 REST services which are reading some common header parameters on the request. I need to use that parameters on my business services. instead of reading that common header parameters on each web service controller (#RestController), Is it possible to read that headers on request filter and make it available on the business services ? If yes, are there any examples to do this ?
You can get request object
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
and access the headers in business services using request object.
Like #Nitin suggest you can pass the request object from your controllers to your services and read the header there. There is no problem with that.
If you still want to read it in a filter and have it available in any #Service you can do as follows:
#Component
#Order(1)
public class HeaderReaderFilter implements Filter {
#Autowired
private HeaderDataHolder headerDataHolder;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
headerDataHolder.setHeaderContent(httpRequest.getHeader("header_field"));
chain.doFilter(request, response);
}
}
#RequestScope
#Component
public class HeaderDataHolder {
private String headerContent;
public String getHeaderContent() {
return headerContent;
}
public void setHeaderContent(String headerContent) {
this.headerContent = headerContent;
}
}
And then have the HeaderDataHolder #Autowired in your service classes. Notice the necessary #RequestScope so you have a different bean for each request.

Logging all request and response in Spring Boot REST service

I use Spring boot and have some REST controllers. I want to logging all request and response. I using external tomacat, not embeded! I write Interceptor:
#Component
#Log4j2
public class LoggingWebMvcInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final ContentCachingRequestWrapper wrapper = new ContentCachingRequestWrapper(request);
log.debug("REQ!!!! {}", wrapper.getReader().lines().collect(Collectors.joining(System.lineSeparator())));
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//log.debug("Response: {}", response);
}
And adding his:
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final LoggingWebMvcInterceptor loggingWebMvcInterceptor;
#Autowired
public WebMvcConfig(LoggingWebMvcInterceptor loggingWebMvcInterceptor) {
this.loggingWebMvcInterceptor = loggingWebMvcInterceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingWebMvcInterceptor);
}
}
But It don't work!
When I try POST request, his logging, but I have error: "Required request body is missing.
What am I doing wrong? I created a wrapper for the request!
I need to completely log all requests (POST, GET, DELETE, PUT) with headers and body and all responses. How can i do this? Any help, please.
Although your problem is not every well understood (not documented well -- for example where this is coming from is not shown Required request body is missing.) but anyways.
For logging purposes, I would not go with an Interceptor as I feel that this is too much work. Instead you could very well create an Aspect with a pointcut defined to around methods annotated with the various Spring controller annotation. The ProceedingJoinPoint#proceed method effectively allows you to grab the response object and the request itself contains all the information needed regarding parameters, IP, methods and so on.
With that in hand, you could then inject a HttpServletRequest in there, thus ending up having all the right tools to perform any logging activities.
Adding the caching wrapper is something very correct indeed if you would like to cache and re-read the HttpServletRequest's body multiple time but I would avoid adding it in the Interceptor/Aspect itself.
According to Baeldung documentation, ContentCachingRequestWrapper class has these limitations:
ContentCachingRequestWrapper class only supports the following:
Content-Type:application/x-www-form-urlencoded
Method-Type:POST
and
We must invoke the following method to ensure that request data is cached in ContentCachingRequestWrapper before using it: requestCacheWrapperObject.getParameterMap();
https://www.baeldung.com/spring-http-logging
You can use a web Filter (javax.servlet.Filter) as :
public class CustomFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain){
//Log actions heres
chain.doFilter(req, resp);}}
Then declare your filter in web.xml as :
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>package.CustomFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Passing a new header in Spring Interceptor

I want to add authentication logic to interceptor. When service is called, interceptor will authenticate. Once authenticated, I want to put a new header in the request say 'header-user': 'john-doe'. But in interceptor, I am unable to do that, when I add to response.setHeader(), nothing happens.
I want to use this new header in actual REST service.
public class AuthInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Authenticate
// Add header
response.setHeader("header-user", "john-doe"); // not working
return true;
}
...
}
If I add Filter, filter is called before Interceptor.
I figured out from Using Spring Interceptor that I can use setAttribute
request.setAttribute("user", "john-doe");
In controller side use,
public String testService(#RequestAttribute("user") String user){

Resources