java.lang.IllegalStateException: getWriter() has already been called for this response
异常的原因是在 Spring MVC 中,如果在拦截器中直接调用 response.getWriter().write()
方法向客户端输出响应数据,就会导致这个异常。
下面的代码会出现这个错误:
public class LoginHandler implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("进入拦截器");
if(request.getSession().getAttribute("employee") == null) {
log.info("未登录");
response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
log.info("返回失败信息");
return true;
}
return true;
}
}
原理:
如果在拦截器中调用getwrite.write就直接会返回数据了,也就是做出了响应。如果拦截器最终放行,那么就会出现业务代码再次进行响应,从而出现两次响应,由于请求只接受一次响应,所以就会认为编写错误,从而会报错。
解决方法:
使用response.getOutputStream().write(),这里需要加上response.setContentType(“application/json;charset=utf-8”);
来保证返回的数据会被浏览器自动解析
public class LoginHandler implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=utf-8");
log.info("进入拦截器");
if(request.getSession().getAttribute("employee") == null) {
log.info("未登录");
response.getOutputStream().write(JSON.toJSONString(R.error("NOTLOGIN")).getBytes(StandardCharsets.UTF_8));
log.info("返回失败信息");
return true;
}
return true;
}
}
为什么response.getOutputStream().write()不会报错
因为这个方法只是将响应数据写入了缓冲区内中,并没有发送到客户端,缓冲区会保留响应数据,直到这次请求最终结束,才将响应发到客户端,从而只产生一次响应
而response.getWriter().write()会直接将响应标记为已完成,因此后续的任何尝试都会抛出IllegalStateException
关于后端最终的返回数据
由于使用了response.getOutputStream().write() ,那么最终能返回的字节数据,而不是字符串,但是由于我们设置了response.setContentType(“application/json;charset=utf-8”);,浏览器会自动将其转换成json字符串。
我们不需要做任何处理,包括js中