Spring Boot 当响应不是String时,HandlerInterceptor接口的afterCompletion方法在响应完全提交之前执行

rxztt3cl  于 2023-01-17  发布在  Spring
关注(0)|答案(1)|浏览(147)

在我的Spring Boot应用程序中,我遇到了一个奇怪的问题。我有一个GET REST端点,它返回一个POJO,如下所示:

@GetMapping(value = "/dto", produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<ReportDto> dto() {
    MultiValueMap<String, String> headers = new HttpHeaders();
    headers.add("Controller processed time",LocalDateTime.now().toString());
    return new ResponseEntity<ReportDto>(new ReportDto(), headers, HttpStatus.OK);
  }

我有一个拦截器,看起来像:

@Slf4j
@Component
public class AuditInterceptor implements HandlerInterceptor {
  @Override
  public void afterCompletion
      (HttpServletRequest request, HttpServletResponse response, Object
          handler, Exception exception) throws Exception {
    Thread.sleep(10000);
    log.info((LocalDateTime.now())+" : Audit event stored");
    response.setHeader("Audit event written time",LocalDateTime.now().toString());
  }

Thread.sleep(10000)用于模拟我们周期性地从审计事件存储中面临的延迟。
按照设计,审计事件应该在响应提交之后写入,这是为了避免在写入事件延迟的情况下客户端的延迟。
令人惊讶的是,客户端仅在10秒后才收到响应(增加的延迟),这意味着响应在执行"afterCompletion"方法之后提交。当响应类型为String时,响应在执行afterCompletion之前提交。我尝试过响应类型Integer、boolean和int。除了String之外,所有其他类型(我尝试过的类型)只有在afterCompletion执行之后才被写入。
我试过不同的客户,他们的行为分别是:

  • Reactwithaxios(同步和异步)-在增加的延迟之后接收响应主体和代码
  • Postman -立即收到响应代码200,但正文是在增加的延迟之后收到的
  • curl-立即打印响应,但连接仅在增加的延迟后关闭

从curl中观察到的行为可以清楚地看出,延迟并不在Http消息转换器中(Spring Boot中默认为Jackson)。
我的问题有一个变通解决方案,如下所示:

@GetMapping(value = "/custom", produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<String> custom() throws JsonProcessingException {
    MultiValueMap<String, String> headers = new HttpHeaders();
    headers.add("Controller processed time",LocalDateTime.now().toString());
    ObjectMapper objectMapper = new ObjectMapper();
    return new ResponseEntity<String>(objectMapper.writeValueAsString(new ReportDto()), headers,
        HttpStatus.OK);
  }

我更希望有正确的解决方案来解决我的问题。感谢任何人能帮助我了解根本原因,以便我们能拿出正确的解决方案。
谢谢你。

nkhmeac6

nkhmeac61#

我相信拦截器是在请求处理程序中调用的,所以如果您想花很多时间,最好在单独的线程或线程池中进行。
同样,如果你想设置响应头,在postHandle中进行,当afterCompletion被调用时,响应已经被提交了。

相关问题