快到年底,项目大部分重要的功能已经开发完成,所以自己有更多free time来分享自己的技术心得以及自己在平常开发过程中所踩过的坑,争取自己今年的博客能突破40篇!
好了,咱们废话少说,开始步入今天的主题,相信大家自己在用spring boot写restful风格的接口时特别是写文件下载或文件导出时会碰到java.lang.IllegalStateException: Cannot call sendError() after the response has been committed这样的bug,很多人可能一脸困惑,就好奇为什么我文件都已经可以正常导出了,为什么在日志中还是会出现这样的报错呢??到底是什么原因导致的呢?当时我也很纳闷,但当我结合了网上的一些大神总结的一些经验经过仔细排查后,就轻轻解决了!下面且听我慢慢讲解。
当时我成功导出文件时,在idea的console上面出现了如下的报错,大家可以看下。
第一张的截图中可以看到日志中打印出了java.lang.IllegalStateException: Cannot call sendError() after the response has been committed这样的报错,从字面上理解抛出的是非法状态的异常,具体原因为提交响应后无法调用sendError()这个方法,从二张截图中可以看出的是http媒体类型不可接受的异常,具体原因为找不到可接受的表示。这两个报错有着一定y的关联,应该是响应在被提交后无法调用sendError()这个方法,所以才进一步导致了找不到可接受的表示这个报错!
那么问题来了,该怎么解决呢?我看了网上的一些经验并结合了自己代码排查后终于发现自己在导出文件接口中存在两个输出对的动作,这两个return语句都是与输出有着紧密的关系,原来我在使用完第一个输出时之后把输出流给关闭掉了,所以也就导致了真正执行第二个输出时出现了response被提交之后不能发送错误请求的bug。具体代码如下所示:
如下截图为第一个输出的代码逻辑:
当时在使用ServletUtil.write(httpServletResponse,httpResponse.bodyStream())(来自于hutool依赖包中的工具类,有兴趣的同学可以了解下)这段代码时我对这个write方法里面的实现逻辑产生了怀疑,认为里面肯定是有关闭输出流的的操作。没想到不看不知道,一看吓一跳,我注意到这个方法里面还真有对输出流的close操作,具体代码如下所示:
既然知道这个方法里面有对输出流的关闭操作,那么
我们先看看外面的输出长什么样子,大家可以看到如下的代码
我们看到第二个输出是输出一个json对象(因为我在类的头部使用了@RestController注解),因为第一个输出是流的方式输出,并且输出成功后又把输出流给关闭了,所以自然就对第二个输出造成了影响!那么怎么办呢?很简单,直接把第二个的 return WebApiResponse.success(“xxxx”)这段代码改为return null就不会报如上这两个报错了。
好的,这周的博客就写到这里,如果自己有写得不清楚的地方,欢迎指出,非常感谢!
参考博客: 解决:java.lang.IllegalStateException: Cannot call sendError() after the response has been committed