Spring MVC + StreamingResponseBody-异常导致HTTP OK

时间:2018-10-05 14:22:27

标签: java spring-mvc spring-boot exception-handling streaming

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

再加上一个控制器建议:

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

}

当我删除throw子句时,它就像一个超级按钮。当我向浏览器输入URL * / test时,可以看到可以下载文件test.csv。可以正确流传输一会儿,我得到200 HTTP代码,可以查看文件了。

但是,引发异常会使代码执行一些奇怪的事情。我没有收到500 HTTP代码,而是200 HTTP代码。在流的末尾,我得到一个“错误的”文本(o_O例外文本)...这是怎么回事?为什么我没有500 HTTP代码。端点未发送完整的数据,但它表示尽管有异常,但还可以。还是我应该使用其他流机制,因为这种机制有问题?

感谢您的帮助!

2 个答案:

答案 0 :(得分:0)

纠正我的理解。您已开始使用流发送数据,因此(我想)有时无法说“等待,删除我已经发送的数据并更改http状态[可能刚开始发送一些数据之前就设置为200]”。因此,当异常发生时,Spring重用OutputStream并将数据从以下位置放入:

return new ResponseEntity<>(exception.getMessage()

进入相同的OutputStream。

一个建议可能是包装可能在try-catch中引发异常并使用IOUtils.closeQuietly的代码,但结果是您将获得5000倍的Hello和流的结尾(因此仍然为200 http状态,即数据您正确发送的是客户端,但末尾没有wrong

示例:

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

答案 1 :(得分:0)

设置一个HTTP预告片以告诉客户端一切正常!或者,如果可以,请改用gRPC-对流进行gRPC错误处理感觉自然得多,客户端连接并为onError,onNext和onComplete事件提供回调。