I have a spring boot application. I change the request body of every post request. Is it possible to modify the request body before the request reaches the controller. Please include an example.
-
take a look at this: https://dzone.com/articles/using-spring-interceptors-your – Zombie Jun 19 '18 at 16:45
-
I think is only allow to set request parameter zombie. In my case modify the request body. – BlueCloud Jun 19 '18 at 16:53
6 Answers
Short Answer
Yes, but not easily.
Details
I know of three options to change the body of a request
"before" it arrives at the handler method in the controller;
- Use AOP to change the request before the method is called.
- Create an HTTP filter.
- Create a custom Spring HandlerInterceptor.
Since you are already using spring-boot, option 3, custom Spring HandlerInterceptor, seems like the best option for you.
Here is a link to a Baeldung Article covering spring HandlerInterceptors.
The Baeldung article is not the full answer for your problem
because you can only read the InputStrem
returned by HttpServletRequest
one time.
You will need to create a wrapper class that extends HttpServletRequest
and wrap every request in your wrapper class within your custom HandlerInterceptor or in a custom Filter (Filter might be the way to go here).
Wrapper Class
- Read the
HttpServletRequest
InputStream in the wrapper class constructor - Modify the body per your requirements.
- Write the modified body to a
ByteArrayOutputStream
. - Use
toByteArray
to retrieve the actualbyte[]
from the stream. - Close the ByteArrayOutputStream (try-with-resources is good for this).
- Override the
getInputStream
method. - Wrap the
byte[]
in a ByteArrayInputStream every time thegetInputStream
is called. Return this stream.
How To Wrap the Request
- In your Filter, instantiate your wrapper class and pass in the original request (which is a parameter to the doFilter method).
- Pass the wrapper to the chain.doFilter method (not the original request).

- 37,124
- 11
- 56
- 82
-
Thanks [DwB](https://stackoverflow.com/users/453005/dwb) i will work and update the status – BlueCloud Jun 19 '18 at 17:09
-
Hi [DwB](https://stackoverflow.com/users/453005/dwb) its throws Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.io.IOException: Stream closed . How can i solve this problem. – BlueCloud Jun 20 '18 at 13:07
-
You can only read from the http message stream one time, ever. that is why you need to wrap the "real" http request in your custom http request wrapper. – DwB Jun 20 '18 at 14:07
-
It seems like HttpServletResponse is immutable. i did modiefied body . how to return stream?? since method " preHandle(HttpServletRequest request, final HttpServletResponse response, final Object handler) " returns boolean.. I even tried by assigning custom HttpServletRequest to original HttpServletRequest. but it does not work. – Rujal Shrestha May 08 '19 at 10:49
-
@RujalShrestha Try to pay attention. This entire answer can be summarized as "wrap the response class in your own class". – DwB May 08 '19 at 14:16
-
@DWB yeah i did the same. do we have any way to reinitialize Spring HttpServletRequest – Rujal Shrestha May 08 '19 at 16:02
-
No. The HttpServletRequest is, as you noted, immutable. All you can do is wrap the request and wrap the response. – DwB May 08 '19 at 16:16
-
have anyone found any solution on this? I tried to implement the same but it's not working. Seeing same error. – Arun Gupta Jun 05 '19 at 07:17
-
4@DwB how do you plan on propagating your wrapped httpServletRequest? The HandlerInterceptor##preHandle just returns a boolean. You can't wrap then propagate your wrapped object further down the dispatch chain. I think Filters might be a better solution if you plan on wrapping the request object since they have the chain.doFilter(request, response); You can pass your wrapped request/responses around this way. – Vladimir Aug 13 '19 at 19:06
-
-
2. Modify the body per your requirements. But how to modify the body? I'm am not able to set the InputStream back to the request. – Roland Feb 12 '20 at 15:14
-
@DwB Hi, actually the way I did is to set the value in controller in a method which is handling the post mapping. I just want to know if it's a recommended way to do like this ? Like changing the incoming request in controller ? I have asked the same question https://stackoverflow.com/questions/62495940/intercepting-incoming-client-request-using-clienthttprequestinterceptor-with-res – Lokesh Pandey Jun 21 '20 at 14:29
-
@DwB Should I override the addInterceptors method inside the custom Logger class in the example? Because it doesn't specify. – Marios Koni Jan 18 '22 at 09:44
-
I don't recognize the reference "custom Logger class" in the context of this question. what do you mean? – DwB Jan 18 '22 at 17:24
Another alternative would be adding an attribute to the HttpServletRequest object. And after that you can read that attribute in the Controller class with @RequestAttribute annotation.
In the Interceptor
@Component
public class SimpleInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws ServletException, IOException {
String parameter = request.getParameter("parameter");
if (parameter == "somevalue") {
request.setAttribute("customAttribute", "value");
}
return true;
}
}
In the Controller
@RestController
@RequestMapping("")
public class SampleController {
@RequestMapping(value = "/sample",method = RequestMethod.POST)
public String work(@RequestBody SampleRequest sampleRequest, @RequestAttribute("customAttribute") String customAttribute) {
System.out.println(customAttribute);
return "This works";
}
}
This has advantage of not modifying the request body.

- 189
- 1
- 4
-
1don't you need specify `SimpleInterceptor` in `SampleController`? what if you define multiple Interceptors? – Lei Yang Jul 28 '21 at 09:22
My answer using HTTP Filter.
RequestFilter.java
@Component
public class RequestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
RequestWrapper wrappedRequest = new RequestWrapper((HttpServletRequest) request);
chain.doFilter(wrappedRequest, response);
}
@Override
public void destroy() {
}
}
RequestWrapper.java
public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
private ObjectMapper objectMapper = new ObjectMapper();
public RequestWrapper(HttpServletRequest request) throws IOException {
// So that other request method behave just like before
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
// Store request body content in 'requestBody' variable
String requestBody = stringBuilder.toString();
JsonNode jsonNode = objectMapper.readTree(requestBody);
//TODO -- Update your request body here
//Sample
((ObjectNode) jsonNode).remove("key");
// Finally store updated request body content in 'body' variable
body = jsonNode.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
public int read() throws IOException {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}

- 893
- 1
- 11
- 16
Here's how I achieved it using the RequestBodyAdvice:
- Create a class that implements RequestBodyAdvice and annotate it with @ControllerAdvice
@ControllerAdvice
public class CustomRequestBodyAdvice implements RequestBodyAdvice {
- You will have to implements 4 methods:
a. support: here, you can control which controller you are targeting, and better which request body by specifying the type of the request body
@Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
log.info("In supports() method of {}", getClass().getSimpleName());
return methodParameter.getContainingClass() == AuthorController.class && type.getTypeName() == AuthorDTO.class.getTypeName();
}
b. beforeBodyReady
<!-- language: lang-js -->
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
log.info("In beforeBodyRead() method of {}", getClass().getSimpleName());
return httpInputMessage;
}
c. afterBodyRead: here it is where you can modify the request body
<!-- language: lang-js -->
@Override
public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
log.info("In afterBodyRead() method of {}", getClass().getSimpleName());
if (body instanceof AuthorDTO) {
AuthorDTO authorDTO = (AuthorDTO) body;
authorDTO.setName("Test");
return authorDTO;
}
return body;
}
d. handleEmptyBody
<!-- language: lang-js -->
@Override
public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
log.info("In handleEmptyBody() method of {}", getClass().getSimpleName());
return body;
}
Source: http://www.javabyexamples.com/quick-guide-to-requestbodyadvice-in-spring-mvc

- 559
- 6
- 10
-
`type.getTypeName() == AuthorDTO.class.getTypeName()` is this correct? I think you should be using `String#equals`. – PEdroArthur Sep 25 '20 at 18:47
-
@PEdroArthur both are correct..the goal here is to target the request body type you wish. – Amy Doxy Sep 28 '20 at 08:24
-
-
would suggest removing " return methodParameter.getContainingClass() == AuthorController.class && type.getTypeName() == AuthorDTO.class.getTypeName();". That is actually a 'compileTime test', not a test you would want run with EVERY REQUEST. – Orubel Jan 05 '22 at 20:59
-
-
How can I modify the body in `beforeBodyRead` ? There are several issues. – Vepir Jun 25 '22 at 20:22
One way to this is by reflection. ProceedingJoinPoint contains the args object passed to method
@Aspect
@Component
public class AopInterceptor {
@Around(value = "@annotation(xyz.rpolnx.spring.web.poc.annotation.AopIntercepExample)")
public Object handler(final ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Object[] args = joinPoint.getArgs();
Class<?> someClass = args[0].getClass();
Field field = someClass.getDeclaredField("custom");
field.setAccessible(true);
field.set(args[0], "custom");
field.setAccessible(false);
return joinPoint.proceed();
}
}
@RestController
public class SimpleController {
@PostMapping("/aop")
@AopIntercepExample
public Person handleAopIntercept(@RequestBody Person nodes) {
return nodes;
}
}
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AopIntercepExample {
}
public class Person {
private String name;
private String id;
private String custom;
}

- 29
- 1
I had this this issue too. This thread helped me a bit and I wanted to post a simpler solution than Bijaya Bhaskar Swain.
package com.thebois.inpassering.adapters.merchant.staffrestrepo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.thebois.inpassering.adapters.merchant.FilterOrders;
import com.thebois.inpassering.adapters.merchant.facilityownerrestrepo.FacilityOwner;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
import javax.servlet.FilterChain;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.springframework.core.annotation.Order;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
@Component
@AllArgsConstructor
@Order(value = FilterOrders.STAFF_REQUEST_ORDER)
public class StaffCreationFilter extends OncePerRequestFilter {
ObjectMapper objectMapper;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
RequestWrapper modifiedRequest = new RequestWrapper(request);
filterChain.doFilter(modifiedRequest, response);
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
return !(request.getServletPath().contains("/staff") && request.getMethod().equals("POST"));
}
private class RequestWrapper extends HttpServletRequestWrapper {
String body;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
String body = new ContentCachingRequestWrapper(request).getReader().lines().collect(Collectors.joining(System.lineSeparator()));
//get your dto
Staff staff = objectMapper.readValue(body, Staff.class);
//edit your dto
long facilityOwnerId = ((Number) request.getAttribute("facilityOwnerId")).longValue();
FacilityOwner facilityOwner = FacilityOwner.builder()
.facilityOwnerId(facilityOwnerId)
.build();
Staff modifiedStaff = Staff.builder()
.facilityOwner(facilityOwner)
.username(staff.getUsername())
.password(new BCryptPasswordEncoder().encode(staff.getPassword()))
.build();
//save your changes to body
this.body = objectMapper.writeValueAsString(modifiedStaff);
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));
ServletInputStream servletInputStream = new ServletInputStream() {
public int read() {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
}

- 109
- 2
- 9