J'ai les choses suivantes dans mon application Spring MVC:corps de réponse du journal après méthode du contrôleur asynchrone Spring MVC
@RestController
public class SomeController {
@GetMapping(value = "/csv", produces = { "text/csv", MediaType.APPLICATION_JSON_VALUE })
public Future someAsyncMethod() {
return CompletableFuture
.supplyAsync(() -> generateCsvSlowly()))
.thenApply(csv -> {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Content-Disposition", "attachment; filename=" + "Filename_.csv");
httpHeaders.add("Cookie", "fileDownload=true; path=/");
return new HttpEntity<>(csv, httpHeaders);
});
}
}
}
Il génère simplement csv, mais si lentement que je dois faire ce asynchrone d'appel.
J'essaie de connecter tout le corps de la réponse de la manière suivante:
@Component
public class LoggingFilter extends OncePerRequestFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggingFilter.class);
private static final AtomicLong ID = new AtomicLong();
static final String SOME_FORMAT_STRING = ...;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
long id = ID.incrementAndGet();
HttpServletResponse responseToUse = response;
if (!(response instanceof ContentCachingResponseWrapper)) {
responseToUse = new ContentCachingResponseWrapper(response);
}
try {
filterChain.doFilter(request, responseToUse);
}
finally {
byte[] responseBodyBytes = ((ContentCachingResponseWrapper) responseToUse).getContentAsByteArray();
LOGGER.info(SOME_FORMAT_STRING, id, responseToUse.getStatus(), responseToUse.getContentType(),
new ServletServerHttpResponse(responseToUse).getHeaders(), new String(bodyBytes, UTF_8));
((ContentCachingResponseWrapper) responseToUse).copyBodyToResponse();
}
}
}
Voici mon gestionnaire d'exception:
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(ApplicationException.class)
@ResponseBody
public ResponseEntity<Status> handleException(ApplicationException exception) {
Status status = new Status();
...
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
...
return new ResponseEntity(status, headers, exception.getHttpCodeMvc());
}
@Override
protected ResponseEntity handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
return handleException(new ApplicationException(ex, ApplicationStatus.GENERIC_ERROR));
}
}
Ici Status
est POJO simple et ApplicationException
est une exception personnalisée classe.
Lorsque generateSlowlyCsv
déclenche une exception, il est traité dans handleException
mais rien n'est consigné et aucun corps n'est renvoyé au client. D'autres méthodes de contrôleur non-asynchrones enregistrent l'erreur (même la même) juste très bien et retournent le corps de la réponse. Lorsque csv est généré (je l'ai vu dans le débogueur) sans erreurs, l'appel se bloque simplement et je ne peux pas trouver où (il revient du futurable). Sans LoggingFilter
tout fonctionne très bien mais sans les bûches bien sûr. Comment puis-je ne pas perdre le corps de la réponse lorsqu'une exception s'est produite et renvoyer csv quand il est généré?
Merci beaucoup!
P.S. De retour Callable
et MvcAsyncTask
de la méthode du contrôleur ne contribue pas trop