近期,查看公司應用日志系統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("成功了,恭喜你."); }
結果:程序正常返回結果,控制台出現異常。
(返回結果中編碼問題,不在本文討論范圍,請忽略)
源碼佐證
二、擴展驗證附驗證結果
/**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輸出流中的內容被發送到客戶端,而且方法返回結果也輸出到客戶端。
————————————————
版權聲明:本文為CSDN博主「常樂_smile」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/li396864285/article/details/78122296