Using Spring @RequestBody and reading HttpServletRequest.getInputStream () afterwards

I am collecting JSON POST request data into an object using Spring @RequestBody and MappingJacksonHttpMessageConverter . However, after that, I would like to read the data in the form of String for additional authentication. But when sorting happened, InputStream in HttpServletRequest empty. As soon as I @RequestBody parameter from the method, reading the POST data in String works as expected.

Should I compromise by abandoning @RequestBody and doing the binding somehow manually or is there a more elegant solution?

+4
source share
3 answers

So basically you need to calculate the hash of the request body. An elegant way to do this is to apply a decorator to an InputStream .

For example, inside the handler method (in this case you cannot use @RequestBody and you need to create the HttpMessageConverter manually):

 @RequestMapping(...) public void handle(HttpServletRequest request) throws IOException { final HashingInputStreamDecorator d = new HashingInputStreamDecorator(request.getInputStream(), secretKey); HttpServletRequest wrapper = new HttpServletRequestWrapper(request) { @Override public ServletInputStream getInputStream() throws IOException { return d; } }; HttpMessageConverter conv = ...; Foo requestBody = (Foo) conv.read(Foo.class, new ServletServerHttpRequest(wrapper)); String hash = d.getHash(); ... } 

where the hash is computed incrementally in the read override of the HashingInputStreamDecorator methods.

You can also use @RequestBody if you create a Filter to apply a decorator. In this case, the decorator can pass the calculated hash to the handler method as a request attribute. However, you need to map this filter to the map to apply it only to requests for a specific handler method.

+2
source

In your urlMapping bean, you can declare a list of additional interceptors:

  <bean id="urlMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="interceptors"> <list> <bean class="org.foo.MyAuthInterceptor"/> </list> </property> </bean> 

These interceptors have access to the HttpServletRequest, although if you are reading from a stream, there is a possibility that the mapper parameter cannot read it.

 public class AuthInterceptor extends HandlerInterceptorAdapter { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ... } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mav) { ... } } 
0
source

If I understand correctly, one common way to use JAX-RS (which is somewhat similar to Spring MVC in relation to binding requests) is to first "bind" to some intermediate raw type (usually byte [], but String also works) and manually bound to the object using basic data binding (Jackson). I often do this to be able to fully customize the handling of data binding errors.

0
source

All Articles