记SpringBoot拦截器报错getWriter() has already been called for this response

1. 问题

在拦截器中返回信息时,使用 response.getWriter() 报错 getWriter() has already been called for this response。这里使用的 getWriter()操作的是字符,所以使用 print()write()都可以(print()wirte区别见 3)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2. 原因分析

这里先确定一个前提,上面的拦截器返回的是 true,还会走后面的拦截器或过滤器,假如返回 false 直接结束,则不会有上述问题。 通过跟踪 getWriter()方法,在类 Response.class 源码中

在这里插入图片描述

可以看到在调用 getWriter()方法时,首先会判断 usingOutputStream,如果是 true,就抛出异常,否则设置 usingWriter=true;。所以调用 getWriter()方法后,会导致:usingWriter=true;

同样在类 Response.class 源码中再查看 getOutputStream()的源码

在这里插入图片描述

可以看到在调用 getOutputStream()方法时,首先会判断 usingWriter,如果是 true,就抛出异常,否则设置 usingOutputStream=true;。所以调用 getOutputStream()方法后,会导致:usingOutputStream=true;

在这样的设定下,就无法同时使用 getWriter()getOutputStream() 方法,调用其中一个之后必然会使其标记为 true,从而调用另一个方法时会报错

之所以两者互相排斥,调用一个方法后不能再调用另一方法,因为 getWriter()是字符流,getOutputStream()是字节流,缓存区不可能同时存在两种格式

3. 解决

在所有的 Filter 和 Interceptor 中,要么都使用 getWriter() 方法,要么都使用 getOutputStream() 方法,不能两者都使用

我这里后面并没有再自定义拦截器或过滤器,推测 SpringBoot 可能默认使用的是 getOutputStream()返回白页等信息,而我并没有 retrun false,同时存在两种格式导致报错。重新修改如下(测试 return false 时下图忘记改成 return true 了,但不影响引出问题):

在这里插入图片描述

这里使用的是 print(),会报错 Not an ISO 8859-1 character,因为 Stream 输出的是二进制流,没有对字符进行编码,Stream 只适用于 ISO 8859-1编码的字符。Writer 输出的是文本的信息, 是进行过系统编码后的

在这里插入图片描述

使用 write() ,将数据用字节传输则正常

在这里插入图片描述

在这里插入图片描述

posted @ 2022-12-21 14:43  凡223  阅读(266)  评论(0编辑  收藏  举报