记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()
,将数据用字节传输则正常