近期,查看公司应用日志系统error错误信息时,发现了大量的nested exception is java.lang.IllegalStateException: getWriter() has already been called for this response 异常。这个错误以前见到过,也解决过。于是想着趁有点空闲,总结下该错误。如有不对,望各位多包容,欢迎交流。
一、应用日志文件中的错误信息
ERROR
977
--- [io-
8686
-exec-
10
] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service()
for
servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: getWriter() has already been called
for
this
response] with root cause
java.lang.IllegalStateException: getWriter() has already been called
for
this
response
at org.apache.catalina.connector.Response.getOutputStream(Response.java:
575
) ~[tomcat-embed-core-
8.5
.
5
.jar:
8.5
.
5
]
IllegalStateException: getWriter() has already been called for this response
从字面意思不难得出错误原因:HttpServletResponse 中的PrintWriter 已经被手动调用过了。所以当servlet 执行到方法结果处理逻辑时,需要将返回值输出到writer 中去,这时发现PrintWriter 已经被调用过。于是servlet 认为这是使用混乱的逻辑错误,于是抛出错误。
根本原因 :在Controller 接口方法中,既手动调用PrintWriter向客户端输出内容,又设置了方法返回值。导致servlet需要两次将结果通过PrintWriter输出到客户端,结果报错。
/**1.反例:验证接口既手动调用PrintWriter输出流flush,又return 返回值.
* 验证结果:客户端ajax请求正常返回,但同时,服务端出现异常,org.springframework.web.util.NestedServletException: Request processing failed;
* nested exception is java.lang.IllegalStateException: getWriter() has already been called for this response
* 结论:方法内已经执行过response输出流write动作.
* servlet再次通过response输出流将返回值向客户端发送时,发现输出流已经被调用过,也就是getWriter()过。于是认为这是使用逻辑混乱,果断报错。
* */
@RequestMapping
(value=
"/checkGetWriterError"
, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public
ServiceResponse<String> checkGetWriterError(HttpServletResponse response)
throws
Exception {
log.info(
"验证接口既手动调用输出流flush,又return 返回值.造成异常!"
);
PrintWriter printWriter = response.getWriter();
printWriter.write(ServiceResponse.ok(
"来自printWriter的返回值"
).toString());
printWriter.flush();
//没有该句也是报一样错.
return
ServiceResponse.ok(
"成功了,恭喜你."
);
}
结果:程序正常返回结果,控制台出现异常。
(返回结果中编码问题,不在本文讨论范围,请忽略)
二、扩展验证附验证结果
验证PrintWriter输出流flush,无返回值
/**2.正例:验证接口仅手动调用PrintWriter输出流flush,无返回值.*/
@RequestMapping
(value=
"/checkGetWriterError2"
, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public
void
checkGetWriterError2(HttpServletResponse response)
throws
Exception {
log.info(
"验证接口手动调用PrintWriter输出流, 无return 返回值. 接口正常执行无异常!"
);
PrintWriter printWriter = response.getWriter();
printWriter.write(ServiceResponse.ok(
"来自printWriter的返回值"
).toString());
printWriter.flush();
}
结果:程序正常返回结果,控制台无异常。
(返回结果中编码问题,不在本文讨论范围,请忽略)
调用ServletOutputStream输出流flush, 无返回值
/**3.正例:验证接口既手动调用ServletOutputStream输出流flush, 无返回值.*/
@RequestMapping
(value=
"/checkGetWriterError3"
, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public
void
checkGetWriterError3(HttpServletResponse response)
throws
Exception {
log.info(
"验证接口手动调用ServletOutputStream输出流, 无return 返回值. 接口正常执行无异常!"
);
ServletOutputStream output = response.getOutputStream();
output.write(ServiceResponse.ok(
"来自ServletOutputStream的返回值"
).toString().getBytes());
output.flush();
}
结果:程序正常返回结果,控制台无异常。
调用ServletOutputStream 输出流flush, 又return 返回值
/**4.反例:验证接口既手动调用ServletOutputStream输出流flush,又return 返回值.*/
@RequestMapping
(value=
"/checkGetWriterError4"
, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public
ServiceResponse<String> checkGetWriterError4(HttpServletResponse response)
throws
Exception {
log.info(
"验证接口手动调用ServletOutputStream输出流, 又执行return 返回值. 接口返回异常!"
);
ServletOutputStream output = response.getOutputStream();
output.write(ServiceResponse.ok(
"来自ServletOutputStream的返回值"
).toString().getBytes());
output.flush();
return
ServiceResponse.ok(
"来自return的返回值."
);
}
结果:程序返回结果不正常,控制台无异常。
不仅ServletOutputStream输出流中的内容被发送到客户端,而且方法返回结果也输出到客户端。