HttpServletResponse的输出问题
1 response的输出流
response对象有getWriter()和getOutputStream()两个方法,它们都是用来向客户端响应用的流对象,不过一个是字符流,另一个是字节流。
? PrintWriter getWriter():获取响应字符流,用来向客户端响应二进制数据,例如图
片;
? ServletOutputStream getOutputStream():获取响应字节流,用来向客户端响应文本
数据,例如HTML代码。
这两者之前只能使用其中一个,也就是说,要么使用字符流,要么使用字节流,不能两者兼得。如果在获取其中一个流对象后,再去获取另一个流,那么就会出现异常。 PrintWriter pw = response.getWriter(); OutputStream out = response.getOutputStream();//抛出异常 OutputStream out = response.getOutputStream(); PrintWriter pw = response.getWriter();//抛出异常 2 刷新输出流
response内部的输出流有8KB的缓冲区,如果缓冲区满了的话,那么response会自动去提交,即把缓冲区内容输出给客户端。这时调用response的isCommited()方法返回的就是true,表示response已经提交过至少一次了。
也可以在缓冲区没有装满时调用response.flushBuffer()方法刷新输出流,把缓冲区中的数据发送到客户端去。同样,这也会导致response的isCommited()方法返回的就是true,表示response已经提交过至少一次了。
其实也可以调用response.getWirter().flush()方法达到与调用response.flushBuffer()相同的效果。这两种方式基本相同!
一旦response的isCommited()方法返回true,这说明服务器已经至少把状态码、响应头等数据发送给客户端了,也就是说已经开始向客户响应了。 response.setContentType(\); PrintWriter out = response.getWriter(); out.print(\大家好\); out.flush();//或者是response.flushBuffer(); System.out.println(\);//可以在这里添加断点,进行测试。但只有FireFox可以看到效果! 但是,这里你要注意一下,不是说服务器向客户端发送了数据,就表示数据一定会显示在浏览器上。因为客户端的浏览器也许会有自己的缓存,在浏览器的缓存没有满之前,浏览器是不会去显示的。例如IE8和Google浏览器就是这样,但FireFox会马上显示数据!
测试得到IE8的缓存为16KB,只要响应数据大于16KB就会在IE8上显示出来。 response.setContentType(\);
for(int i = 0; i < 1024 * 16 + 1; i++) { } System.out.println(\); //可以在本行添加断点,进行测试 response.getWriter().print(\); 重定向
1 什么是重定向
重定向与请求转发很相似,但有本质上的区别。请求转发需要使用RequestDispatcher类的forward()方法,而重定向需要使用response对象的sendRedirect()方法。
// AServlet public void doGet(HttpServletRequest request, HttpServletResponse response) } // BServlet public void doGet(HttpServletRequest request, HttpServletResponse response) } throws ServletException, IOException { response.setContentType(\); PrintWriter out = response.getWriter(); out.print(\); throws ServletException, IOException { response.sendRedirect(\);
2 重定向的原理
现在你应该已经会用重定向了,但你还不知道重定向的原理。我们先来说说重定向的原理,然后再去与请求转发比较。
response.sendRedirect()方法其实就是向客户端的响应!没错,调用这个方法就已经是向客户端响应了。那你可能会问,为什么响应了之后又会看到BServlet的响应结果呢?因为状态码为302,这表示命令客户端浏览器马上再去向服务器发出另一个请求,请求的资源为BServlet。
当响应状态码为302时,响应头信息中需要还有一个响应头信息,这个头信息名为Location。这相当于你到我家来找我,我家门上有个纸条,上面写着“302,我已经搬家,请马上到XXX来,可以找到我”。302表示“以搬家”,而XXX表示“新地址”。其中响应头信息Location就是“新地址”了。
请求AServlet
请求BServlet
没错,重定向其实就是两个请求,而且两个请求还是连续的,因为速度很快,所以你看不出这是两个请求。
你也许已经留意到了,浏览器的地址栏中显示的不在是AServlet,而是BServlet。你可以尝试去访问http://www.sun.com,但最终浏览器地址栏中显示的是http://www.oracle.com/us/sun/index.htm。这就是重定向!
因为是两个请求,所以你在AServlet中使用request.setAttribute()保存的数据,在BServlet中是获取不到的!这一点与请求转发是不同的!
3 重定向与请求转发的比较
重定向的理解:你到我家找我借钱(第一个请求),但是我已经搬家,新住户把我的新地址告诉了你(服务器返回302响应,以及Location响应头),你再去新地址找我(第二个请求),我把钱借给了你(第二个响应,200)。 重定向时序图
注意一下,重定向其实一共是两个请求。因为是两个请求,所以不能使用request在两个请求中间传递数据!!!(两个HTTP请求,不能使用request.setAttribute()和request.getAttribute()传递数据)。因为服务器会为每个请求创建新的request和response对象!
观察浏览器地址栏中的地址会发生变化,这是因为浏览器重定向到新的地址了。 第二个请求一定是GET,不可能是POST!!!因为只有提交POST表单才是POST请求,而第二个请求是浏览器通过第一个请求的响应结果直接发出的,所以一定是GET。所以调用的一定是BServlet的doGet()方法。
请求转发的理解:你到我家找我借钱(第一个请求),但是我已经搬家,新住户让你稍等,然后新住户去我的新地址把我找来(请求转发),然后我们见面了,我把钱借给了你。(响应成功,200)
请求转发时序图
因为只有一个请求,所以request对象可以重用!可以在BServlet中使用