5.2.8 用forward方法请求转发网络资源
forward方法和include方法类似。只是forward方法的作用是转发网络资源(HTML、JSP、Servlet、图象等)。下面的例子演示了如何使用forward方法转发网络资源。
例子 : 用forward方法请求转发网络资源
1. 实例说明
本例使用了一个ForwardingServlet类分别转入IncludedServlet、IncludedHtml.html和图像文件。
2. 编写ForwardingServlet类
ForwardingServlet类负责转入由forward请求参数指定的网络资源。该类的实现代码如下:
package chapter5;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ForwardingServlet extends HttpServlet
{
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
// PrintWriter out = response.getWriter();
String forward = request.getParameter("forward");
if (forward != null)
{
RequestDispatcher rd = getServletContext().getRequestDispatcher(forward);
rd.forward(request, response);
// rd.forward(request, response); // 多次调用forward方法也会抛出异常
}
}
}
3. 配置ForwardingServlet类
ForwardingServlet类的配置代码如下:
<servlet>
<servlet-name>ForwardingServlet</servlet-name>
<servlet-class>chapter5.ForwardingServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ForwardingServlet</servlet-name>
<url-pattern>/ForwardingServlet</url-pattern>
</servlet-mapping>
4. 转入IncludedServlet
在浏览器地址栏中输入如下的URL:
http://localhost:8080/demo/ForwardingServlet?forward=/IncludedServlet
浏览器中显示的结果如图5.12所示。
图5.12 转入IncludedServlet
5. 转发图象文件
假设<Web根目录>\images目录中有一个abc.jpg文件。在浏览器地址栏中输入如下的URL:
http://localhost:8080/demo/ForwardingServlet?forward=/images/abc.jpg
浏览器将显示abc.jpg文件的内容。
现在将PrintWriter out = response.getWriter()前面的注释去掉,然后再次访问上面的URL。这时在浏览器中没有将图象显示出来,而是抛出一个java.lang.IllegalStateException异常(如果在浏览器中仍然显示出abc.jpg的内容,是因为浏览器缓存的原因,读者可以将浏览器的缓存清空,再次访问上面的URL)。抛出这个异常的原因是当forward转发的是二进制网络资源时(如图象文件),会使用ServletOutputStream对象直接将字节流输出到客户端,而在ForwardingServlet类的service方法中已经使用了getWriter方法来获得PrintWriter对象,因此,在调用forward方法时,就无法再使用ServletOutputStream对象了,所以就会抛出异常。
6. 多次调用forward
将第二条rd.forward(request, response)语句的注释去掉,然后在浏览器地址栏中再次输入如下的URL:
http://localhost:8080/demo/ForwardingServlet?forward=/IncludedServlet
浏览器将抛出一个java.lang.IllegalStateException异常,这说明无法多次调用forward方法,也就是说forward方法只能调用一次。
7. 程序总结
从图5.12所示的信息可以看出,虽然在浏览器地址栏中仍然是刚才输入的URL,但是所显示的内容却是IncludedServlet类所输出的内容,而且所输出的URL是IncludedServlet的访问路径,而不是ForwardingServlet的访问路径,并且正常输出了中文信息。因此可以断定。在IncludedServlet类中修改HTTP响应消息头的字段值起作用了(成功使用setContentType方法将服务端编码设为了UTF-8),并且在被调用者(IncludedServlet)中获得的URL就是自身。这一点是forward和include的区别之一。
在使用forward方法转发网络资源时应注意以下六点:
(1)在调用forward方法之前,输出缓冲区中的信息会被清空。也就是说,使用forward方法进行请求转发时,只可能输出被调用者的信息。读者可以在调用forward方法之前使用PrintWriter对象输出一些信息,看看会发生什么情况。
(2)如果在调用forward方法之前,已经将缓冲区中的数据发送到客户端,在调用forward方法时会抛出IllegalStateException异常。这个异常消息会在启动Tomcat的控制台中显示,而在浏览器中仍然会输出已经发送到客户端浏览器的消息。如下面的代码将在浏览器中正常显示“abcd”,但是会在控制台显示抛出的异常:
PrintWriter out = response.getPrintWriter();
out.println("abcd");
out.flush();
RequestDispatcher rd =
getServletContext().getRequestDispatcher("/servlet/IncludedServlet");
rd.forward(request, response);
(3)在调用者和被调用者中设置响应消息头都不会被忽略。而调用include方法时,只有在调用者中设置的响应消息头才会生效。
(4)Servlet引擎会根据RequestDispatcher对象所包含的网络资源对HttpServletRequest对象中的请求消息进行调整。而使用include方法时Servlet引擎并不调整这些消息。
(5)forward方法只能使用一次,否则会抛出IllegalStateException异常,而include方法可以多次使用。
(6)除了使用HttpRequestServlet接口的getRequestDispatcher方法获得RequestDispatcher对象外,还可以通过ServletContext接口的getNamedDispatcher方法获得RequestDispatcher对象,可以向该方法传递一个Servlet的名称。如在service方法中可以使用如下的代码来获得RequestDispatcher对象:
RequestDispatcher rd =
this.getServletContext().getNamedDispatcher("IncludedServlet");
其中IncludedSevlet是Servlet类的名称。