Utilizzando @RequestBody di primavera e la lettura HttpServletRequest.getInputStream () in seguito
-
28-09-2019 - |
Domanda
Sono la mappatura dei dati JSON post di mia richiesta in un oggetto di annotazione utilizzando @RequestBody
di primavera e MappingJacksonHttpMessageConverter
. Tuttavia dopo che mi piacerebbe leggere i dati in forma String
di fare qualche ulteriore autenticazione. Ma quando lo smistamento è avvenuto, il InputStream
in HttpServletRequest
è vuoto. Una volta che tolgo il parametro @RequestBody
dal metodo della lettura dei dati POST in un opere String
come previsto.
Devo compromesso rinunciando al @RequestBody
e facendo la vincolante in qualche modo manualmente o c'è una soluzione più elegante?
Soluzione
Quindi, in sostanza è necessario calcolare un hash del corpo della richiesta. Il modo elegante per farlo è quello di applicare un decoratore al InputStream
.
Ad esempio, all'interno di un metodo del gestore (in questo caso non è possibile utilizzare @RequestBody
e necessità di creare manualmente HttpMessageConverter
):
@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();
...
}
dove hash viene calcolato in modo incrementale in metodi read
sovrascritto di HashingInputStreamDecorator
.
È inoltre possibile utilizzare @RequestBody
se si crea un Filter
per applicare il decoratore. In questo caso decoratore può passare l'hash calcolato al metodo del gestore come attributo richiesta. Tuttavia, è necessario mappare attentamente questo filtro da applicare solo alle richieste a metodo di gestore specifico.
Altri suggerimenti
Nel vostro fagioli urlMapping si può dichiarare lista di intercettori supplementari:
<bean id="urlMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<bean class="org.foo.MyAuthInterceptor"/>
</list>
</property>
</bean>
Gli intercettori hanno accesso a HttpServletRequest, anche se si legge dal flusso è probabile che il parametro mapper non sarà in grado di leggerlo.
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) {
...
}
}
Se comprendo questo, un modo corretto comune utilizzato con JAX-RS (che è simile a Spring MVC per le richieste di legame) è alla prima "legare" in qualche tipo prima intermedia (solitamente byte [], ma String anche lavori), e associare manualmente da quello a oggetto, utilizzando sottostante raccoglitore di dati (Jackson). Spesso faccio questo per essere in grado di personalizzare completamente la gestione degli errori di associazione dati.