Spring MVC + StreamingResponseBody - 异常导致HTTP OK

Posted

技术标签:

【中文标题】Spring MVC + StreamingResponseBody - 异常导致HTTP OK【英文标题】:Spring MVC + StreamingResponseBody - exception causes HTTP OK 【发布时间】:2019-03-11 02:04:11 【问题描述】:

我在 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 代码。端点发送的数据不是完整的,但它说尽管有例外情况都可以。或者我应该使用其他一些流媒体机制,因为这个机制有问题?

感谢您的帮助!

【问题讨论】:

【参考方案1】:

设置一个 HTTP 预告片告诉客户端一切正常!或者如果你可以切换到 gRPC - gRPC 对流的错误处理感觉更自然,客户端连接并为 onError、onNext 和 onComplete 事件提供回调。

【讨论】:

【参考方案2】:

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

return new ResponseEntity&lt;&gt;(exception.getMessage()

进入同一个输出流。

一个建议可能是包装可能在 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;
     

【讨论】:

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

以上是关于Spring MVC + StreamingResponseBody - 异常导致HTTP OK的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC学习—MVC的介绍以及Spring MVC的入门案例

spring mvc怎么存cookie

Spring MVC系列初识Spring MVC

spring mvc中 Circular view path 问题

Spring MVC 框架学习---- 我的第一个 Spring MVC 程序

springmvc 3.0啥时间发部