Reputation: 4480
I'm mapping my request's JSON POST data into an object using Spring's @RequestBody
annotation and MappingJacksonHttpMessageConverter
. However after that I'd like to read the data in String
form to do some additional authentication. But when the marshalling has happened, the InputStream
in HttpServletRequest
is empty. Once I remove the @RequestBody
parameter from the method the reading of POST data into a String
works as expected.
Do I have to compromise by giving up the @RequestBody
and doing the binding somehow manually or is there a more elegant solution?
Upvotes: 6
Views: 12829
Reputation: 116522
If I understand this correctly, one common way used with JAX-RS (which is somewhat similar to Spring MVC with respect to binding requests) is to first "bind" into some intermediate raw type (usually byte[], but String also works), and manually bind from that to object, using underlying data binder (Jackson). I often do this to be able to fully customize error handling of data binding.
Upvotes: 0
Reputation: 242696
So, basically you need to compute a hash of the request body. The elegant way to do it is to apply a decorator to the InputStream
.
For example, inside a handler method (in this case you can't use @RequestBody
and need to create 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 hash is computed incrementally in overriden read
methods of HashingInputStreamDecorator
.
You can also use @RequestBody
if you create a Filter
to apply the decorator. In this case decorator can pass the computed hash to the handler method as a request attribute. However, you need to map this filter carefully to apply it only to the requests to specific handler method.
Upvotes: 2
Reputation: 31795
In your urlMapping bean you can declare 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>
Those interceptors have access to HttpServletRequest, though if you read from the stream the chances are that parameter mapper won't be able to 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) {
...
}
}
Upvotes: 0