SpringMVC(8)将response使用flush提交响应后重定向报错:java.lang.IllegalStateException: 提交响应后无法调用sendRedirect()。

本文探讨了在Java Web开发中使用flush后尝试重定向所遇到的问题及解决方案。通过对比不同方法的效果,最终找到了既能输出提示信息又能实现重定向的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

好吧,这算是我的奇思妙想之一

问题描述

代码片段如下:

@RequestMapping("logout")//表示user/logout.do(有后缀)
	public String logout(HttpServletRequest request,HttpServletResponse response) {
		//删除当前的session
		request.getSession().invalidate();//销毁当前session
		
		//获取响应字符输出流
		PrintWriter out = null;
		try {
			out = response.getWriter();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if(out!=null) {
			out.print("<script type='text/javascript'>");
			out.print("alert('退出成功!');");
			out.print("</script>");
			out.flush();//提交响应流,已提交的响应无法被修改,默认提交请求头200
		}
		return "redirect:showLogin.do";
	}
  • 预期的作用是:先在页面弹出提示框,然后重定向到showLogin.do页面
  • 实际的作用是:报错: 提交响应后无法调用sendRedirect()。

报错

  • java.lang.IllegalStateException: 提交响应后无法调用sendRedirect()。

解决方案1

  • 将重定向变为转发就可以正常使用了
return "showLogin.do";

原理分析

  • 你如果用了response.getWriter().flush()方法,容器的实现会认为你要输出的内容无法确定大小(因为你已经先flush先输出一部分内容了),所以就采用了chunked编码方式输出。
    那么就没有Content-Length: xxx, 而是多了一个 Transfer-Encoding: chunked,表示分段输出。
  • 如果你不用flush,那么返回的http头里是包含 Content-Length: xxx 这样的,表示返回的全文长度。
  • 如图
    在这里插入图片描述
    在这里插入图片描述

out.flush()意味着什么?

  • flush表示响应已提交,默认提交请求头状态码200,原因如下:
out.flush();
//尝试使用reset重置状态码
response.reset();//报错:IllegalStateException: 已经提交响应后无法调用reset()
  • 当一个响应被提交时,这意味着至少头部已经被发送到客户端.
  • 当响应已经提交时,您无法设置/更改标头,因为为时已晚.

那么如何终止分块编码【Transfer-Encoding: chunked】?

  • 一般服务器响应消息都会在HTTP响应头部加上Content-Length,这样客户端就知道要接收多少数据。
    但是,分块编码无法在响应头部设置Content-Length,因为传输的数据不知道会有多少个。
    所以,也就不知道数据会在何时终止
  • 在头部加入 Transfer-Encoding: chunked 之后,就代表这个报文采用了分块编码。这时,报文中的实体需要改为用一系列分块来传输。
  • 最后一个分块长度值必须为 0,对应的分块数据没有内容,表示实体结束。
    当包的大小为0时,当客户端收到一个包大小为0的chunk包时,表示包传输完了。

再次尝试【结果以失败告终】

指定响应码302

out.print("<script type='text/javascript'>");
out.print("alert('退出成功1!');");
out.print("</script>");
response.setStatus(302);//临时定向响应码
out.flush();//提交响应流,已提交的响应无法被修改
  • 使用上述代码,可以实现提交数据,并将当前状态码改为302
  • 但是,没有办法指定重定向的地址
    在这里插入图片描述

指定响应码302,指定重定向的地址

out.print("<script type='text/javascript'>");
out.print("alert('退出成功1!');");
out.print("</script>");
response.setStatus(302);//临时定向响应码
response.setHeader("Location", "showLogin.do");//showLogin.do 代表重定向的地址
out.flush();//提交响应流,已提交的响应无法被修改
  • 使用上述代码,可以实现当前状态码改为302,并且重定向成功
  • 但是数据不会打印出来,而且还显示分块编码
    在这里插入图片描述

指定响应码302,指定重定向的地址,指定浏览器接受的数据大小

out.print("<script type='text/javascript'>");
out.print("alert('退出成功1!');");
out.print("</script>");
response.setStatus(302);//临时定向响应码
response.setHeader("Location", "showLogin.do");//showLogin.do 代表重定向的地址
response.setTrailerFields(null);//没啥效果可以忽略
//setContentLength,表示响应后,浏览器接受的数据大小
//设置0,结束分块编码,提交后,不处理任何数据
response.setContentLength(0);
out.flush();//提交响应流,已提交的响应无法被修改
  • 使用上述代码,可以实现当前状态码改为302,并且重定向成功
  • 但是数据不会打印出来,不再显示分块编码
    在这里插入图片描述

个人总结

  • 重定向的过程需要进行两次请求响应
  • 第一次响应回来的时候,如果指定了状态码是302且Location重定向的地址,则会直接跳转到指定的Location地址,不会处理第一次响应的信息。
  • 第一次响应回来的时候,如果指定了状态码是302,但没有指定重定向地址,会按照状态码200的特点进行处理,处理响应的信息,但是处理完之后,会滞留在当前的页面,不会跳转。
  • 相同路径,提交过一次响应头之后,不允许再次提交
    即,当一个响应被提交时,这意味着至少头部已经被发送到客户端.
    当响应已经提交时,您无法设置/更改标头,因为为时已晚.
  • 建议在使用转发的时候进行,因为转发的状态码是200,和flush提交后的默认状态码一致

不死心的垂死挣扎

2022-10-07放弃挣扎1

URL realUrl = new URL("http://localhost:8080/testSpringMVC6/user/logout.do");
// 打开和URL之间的连接
URLConnection connection = realUrl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.setConnectTimeout(30 * 1000);
// 建立实际的连接
connection.connect();//
String location = connection.getHeaderField("Location");
System.out.println(location);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值