2017-08-29 22 views
1

た後、私はSpring MVCのアプリケーションで、以下のものを持っている:ログ応答ボディ非同期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); 
      }); 
     } 
    } 
} 

だから、単にので、ゆっくりと、私はこの呼び出しを非同期に行うために持っていることをcsvファイルを生成しますが。

私は次のようにすべてのレスポンスボディのログを記録しようとしている:ここで

@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(); 
     } 
    } 

} 

は私の例外ハンドラです:

@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)); 
    } 
} 

ここStatusは、単純なPOJOで、ApplicationExceptionは、カスタム例外ですクラス。

generateSlowlyCsvはそれがhandleExceptionで処理されますが、何ものが記録され、何のボディがクライアントに返されません取得していない例外がスローされます。他の非非同期コントローラメソッドはエラー(同じものでも)を記録し、応答本体を返します。 csvが生成されたとき(私はデバッガでそれを見た)エラーがなくなり、コールがハングアップし、どこからでも見つけられませんでした。 LoggingFilterがなければ、すべてうまくいくが、もちろんログはない。

例外が発生したときに応答ボディを緩めて、生成時にcsvを返す方法はありますか?どうもありがとうございました!

P.S.私は次のようなもので終わっすぎ

答えて

0

を助けないコントローラメソッドからCallableMvcAsyncTaskを返すこと:

package com.ololo.filter; 

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.core.MethodParameter; 
import org.springframework.http.HttpHeaders; 
import org.springframework.http.HttpOutputMessage; 
import org.springframework.http.MediaType; 
import org.springframework.http.converter.HttpMessageConverter; 
import org.springframework.http.server.ServerHttpRequest; 
import org.springframework.http.server.ServerHttpResponse; 
import org.springframework.http.server.ServletServerHttpRequest; 
import org.springframework.http.server.ServletServerHttpResponse; 
import org.springframework.web.bind.annotation.ControllerAdvice; 
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; 
import org.springframework.web.util.ContentCachingRequestWrapper; 

import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.OutputStream; 
import java.util.concurrent.atomic.AtomicLong; 

import javax.servlet.http.HttpServletRequest; 

import static java.lang.System.lineSeparator; 
import static java.nio.charset.StandardCharsets.UTF_8; 

@ControllerAdvice 
public class LoggingAdvice implements ResponseBodyAdvice { 

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggingAdvice.class); 
    private static final AtomicLong ID = new AtomicLong(); 

    private static final String SOME_RESPONSE_MESSAGE_FORMAT; 

    @Override 
    public boolean supports(MethodParameter returnType, Class converterType) { 
     return true; 
    } 

    @Override 
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { 
     long id = ID.incrementAndGet(); 

     ServletServerHttpResponse responseToUse = (ServletServerHttpResponse) response; 
     HttpMessageConverter httpMessageConverter; 
     LoggingHttpOutboundMessageWrapper httpOutputMessage = new LoggingHttpOutboundMessageWrapper(); 
     try { 
      httpMessageConverter = (HttpMessageConverter) selectedConverterType.newInstance(); 
      httpMessageConverter.write(body, selectedContentType, httpOutputMessage); 
      LOGGER.info(SOME_RESPONSE_MESSAGE_FORMAT, id, responseToUse.getServletResponse().getStatus(), responseToUse.getServletResponse().getContentType(), 
        responseToUse.getHeaders(), httpOutputMessage.getResponseBodyInString()); 
     } catch (InstantiationException | IllegalAccessException | IOException e) { 
      e.printStackTrace(); 
     } 

     return body; 
    } 

    private static final class LoggingHttpOutboundMessageWrapper implements HttpOutputMessage { 
     private HttpHeaders httpHeaders = new HttpHeaders(); 
     private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 

     @Override 
     public OutputStream getBody() throws IOException { 
      return byteArrayOutputStream; 
     } 

     @Override 
     public HttpHeaders getHeaders() { 
      return httpHeaders; 
     } 

     public String getResponseBodyInString() { 
      return new String(byteArrayOutputStream.toByteArray()); 
     } 
    } 

} 

そして、はい、私は知っている、それは地獄のようにひどいですが、少なくともそれはすべてのために働いています@ResponseBodyコントローラメソッド。私は(少なくとも今のところは)私には別のものが必要だとは思わない。私は非同期フィルタを作成しようとしましたが、AsyncContextAsyncListenerを追加できませんでした(詳細はthe following questionを参照してください)。これがあなたを助けることを願っています

関連する問題