Spring MVC + StreamingResponseBody - 异常导致 HTTP OK

use*_*782 9 java streaming exception spring-mvc spring-boot

我在 Spring MVC 控制器中遇到了一个关于 StreamingReponseBody 的奇怪问题。简单的控制器代码:

    @RestController
    public class ServerController {

    @GetMapping("test")
    StreamingResponseBody test(HttpServletResponse response) {
        response.setContentType("text/csv");
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"test.csv\"");


        return outputStream -> {
            for (int i = 0; i < 5000; i++) {
                outputStream.write("Hello".getBytes());
                outputStream.write("\n".getBytes());
            }

            try {
                Thread.sleep(2000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (true) {
                throw new RuntimeException("wrong");
            }
            for (int i = 0; i < 5000; i++) {
                outputStream.write("Hello2".getBytes());
                outputStream.write("\n".getBytes());
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

再加上一个控制器建议:

@ControllerAdvice
public class CustomControllerAdvice {

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleInternalServerError(Exception exception) {
        System.out.println("Unexpected error " + exception);
        return new ResponseEntity<>(exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
Run Code Online (Sandbox Code Playgroud)

}

当我删除 throw 子句时,它就像一个魅力。当我在浏览器中输入 URL */test 时,我可以看到可以下载文件 test.csv。它会正确传输一段时间,我会收到 200 HTTP 代码,并且可以查看该文件。

然而抛出异常会导致代码做一些奇怪的事情。我没有收到 500 HTTP 代码,而是收到 200 HTTP 代码。在流的末尾,我得到一个“错误”文本(异常文本 o_O)...这里发生了什么?为什么我没有收到 500 HTTP 代码。端点发送的数据不完整,但它表示尽管有异常,但一切正常。或者我应该使用其他某种流媒体机制,因为这个机制有问题?

谢谢你的帮助!

mat*_*mik 0

纠正我的理解。你开始使用流发送数据,所以你(我猜)不能在某个时刻说“等待,删除我已经发送的数据并更改http状态[可能在开始发送一些数据之前设置为200]”。因此,当异常发生时,Spring 重用 OutputStream 并从以下位置放入数据:

return new ResponseEntity<>(exception.getMessage()

到同一个输出流中。

一个建议可能是包装可能在 try-catch 和 use 中抛出异常的代码,IOUtils.closeQuietly但效果是您将获得 5000x 的 Hello 和流结束(因此仍然是 200 http 状态,您正确发送的数据位于客户端,但wrong最后没有)

例子:

if (true) {
    try {
        throw new RuntimeException("wrong");
     } catch (RuntimeException e) {
        IOUtils.closeQuietly(outputStream); #apache
        throw e;
     }
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,这正是我正在尝试做的。那么是否有一种更能感知错误的解决方案来将一些动态数据返回给客户端呢?首先流式传输响应,然后发送标头,或者仅在一切正常时才返回所有内容? (2认同)
  • 没有人知道答案吗? (2认同)
  • 对此仍然没有答案吗?我现在也面临着同样的问题。 (2认同)