3

How I can override @RequestBody content in spring boot before it reaches to the controller?

  1. I know that there is WebMvcConfigurer and HandlerInterceptorAdapter classes to handle request before controller.

  2. Also I've googled for RequestBodyAdviceAdapter as well.

There are several links that didn't work for spring boot.

How to read request.getInputStream() multiple times

How to modify request body before reaching controller in spring boot

Now can I read input stream into string, make some modification and set back into input stream for the controller?

gogagubi
  • 965
  • 1
  • 15
  • 36
  • Any Tip will be helpful – gogagubi Dec 31 '19 at 11:06
  • Try with AOP. It is very easy to create a method that will be called `before` the controller. Here are the keywords : `Aspect`, `@Before`. I can’t provide more details, Maybe later in the day, show us what you tried with Aspect – RUARO Thibault Dec 31 '19 at 13:24
  • I think your best bet would be to add a @ControllerAdvice/@RestControllerAdvice class and implement RequestBodyAdvice. If you know the @RequestBody Object then in "afterBodyRead" cast & set the specific properties. – Lipu Dec 31 '19 at 16:00
  • @RUAROThibault my goal is to change object name(capitalize) in json string, before it reaches to the spring boot controller, So that spring will map the object correctly by name. I tried AOP before and this is no point for me, because I am getting mapped object in aop(proceedingJoinPoint.getArgs()) and the value is set to null already. I think I need before Aop. – gogagubi Dec 31 '19 at 16:12
  • @Lipu Thanks for your response. I tried ControllerAdvice and RequestBody but I need the way to access request body(json string) as well. My controller receives json object(java class) and property names are with lower case letters. I want access to that json before spring boot will try to map it to the object and modify property names as java and spring boot expects – gogagubi Dec 31 '19 at 16:23
  • Then you should see the `javax.servlet.Filter`, might be given you the ability to act on the request before anything is reached. – RUARO Thibault Dec 31 '19 at 16:54
  • I'm not clear why you need to modify the object in question - if you have an object mapping to/from JSON, can you not just add the `@JsonProperty("lowercasename")` annotation on the field/setter of the POJO you care about? – josh.trow Jan 01 '20 at 23:11
  • as @josh.trow suggested, it will be much easier if you can update the java class. If you cannot then write custom Serializer&Deserializer and add them to ObjectMapper configuration. – Lipu Jan 02 '20 at 11:09

2 Answers2

6

Solution 1: (In my opinion better solution) As suggested in comment try using the @JsonProperty or custom De-/Serializer for the object.

Solution 2: Add a @ControllerAdvice and implement RequestBodyAdvice and override the beforeBodyRead as

@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
    InputStream body = inputMessage.getBody();
    String bodyStr = IOUtils.toString(body, Charset.forName("UTF-8"));
    /*
    Update bodyStr as you wish
    */
    HttpInputMessage ret = new MappingJacksonInputMessage(new ByteArrayInputStream(bodyStr.getBytes()), inputMessage.getHeaders()); //set the updated bodyStr
    return ret;
}
Lipu
  • 607
  • 1
  • 5
  • 17
  • Thanks man. Solution 2 is exactly what I wanted to do. Solution 1 is perfect and I know about it. But I have xsd based classes(generated from xsd schema) and I didn't want to change them manually and apply custom annotations. – gogagubi Jan 02 '20 at 21:03
  • Thanks! Found this answer after multiple unsuccessful attemts of OncePerRequestFilter and HandlerInterceptor implementations. This is perfect for my use-case: checking if fhe content signature matches the Signature header value. – C-Shark Aug 23 '21 at 12:33
  • `@RequestBody` is empty in my controller after returning ret (didn't modify string yet). *Solution*: Put `ByteArrayInputStream` into `PushbackInputStream`. – Vepir Jun 25 '22 at 21:01
0

This looks like a good use case for a Servlet Filter. The filter will receive the request before the Controller, allow you to modify the request, then you can pass the request down the filter chain for processing by your Controller.

Example: https://www.baeldung.com/spring-boot-add-filter

You will need to write a custom HttpServletRequestWrapper. In this wrapper class constructor will receive the request. Read the InputStream into a variable (at this point you can modify the stream). Also override the getInputStream() and getReader() methods to return your modified stream (not the original).

In your filter, create a new instance of your wrapper and pass the incoming request into the constructor. Once you have made your modifications, pass the wrapped request into chain.doFilter(myWrappedRequest, response) to allow downstream controllers to process it.

This is an example of how to create the wrapper and use it in the filter: https://howtodoinjava.com/servlets/httpservletrequestwrapper-example-read-request-body/

KevinB
  • 1,102
  • 2
  • 8
  • 19
  • thanks for your answer. I looked filters but I don't see opportunity how read Input stream, modify and set it back. Do you have example to do it? – gogagubi Jan 01 '20 at 17:21
  • I modified my response to show you how to use a custom HttpServletRequestWrapper to allow you to modify the inputstream and then pass it into the chain. – KevinB Jan 01 '20 at 23:07
  • I think I tried this example but didn't get the desired result. – gogagubi Jan 02 '20 at 21:00