一、RequestDispatcher
RequestDispatcher实例对象是由Servlet引擎创建的,它用于包装一个要被其他资源调用的资源,例如Servlet、HTML文件,JSP文件等,并可以通过其中的方法将客户端的请求转发给所包装的资源。RequestDispatcher接口定义了forward和include方法,forward用于将请求转发到RequestDispatcher对象封装的资源,include用于将RequestDispatcher对象封装的资源作为当前响应内容的一部门包含进来。
ServletContext接口中定义了两个用于获取RequestDispatcher对象的方法:
getRequestDispatcher方法:返回包装了某个路径所指定的资源的RequestDispatcher对象,传递给该方法的路径字符串必须以“/”开头,“/”代表当前Web应用程序的根目录(虚拟目录)。WEB-INF目录中的内容对RequestDispatcher对象是可见的,所以,传递给getRequestDispatcher方法的资源可以是WEB-INF目录中不能被外界访问的文件。
getNamedDispatcher方法:返回包装了某个Servlet或JSP文件的RequestDispatcher对象,传递给该方法的参数是在Web应用程序部署描述符中为Servlet或JSP文件指定的友好名称。
在ServletRequest接口中也定义了一个getRequestDispatcher方法,它与ServletContext.getRequestDispatcher的区别是:传递给这个方法的参数除了可以采用“/”开头的路径字符串,还可以采用非“/”开头的相对路径。
二、用include方法实现资源包含
1、include方法
该方法用于将RequestDispatcher对象封装的资源作为当前响应内容的一部门包含进来,被包含的Servlet程序不能改变响应消息的状态码和响应头。注意一点是:URL路径一直是调用者程序的路径。
2、应用细节
(1)被包含的资源是Servlet程序
IncludingServlet.java
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 这是包含其他资源的Servlet
* @author Lzj
*
*/
public class IncludingServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html; charset=GB2312");
PrintWriter out = response.getWriter();
String china = "中国";
// (1)必须要以/开头,省略则报错
RequestDispatcher rd = getServletContext().getRequestDispatcher
("/servlet/IncludedServlet?p1=" + china);
out.println("before including" + "<br>");
rd.include(request, response);
out.println("after including" + "<br>");
}
}
IncludedServlet.java
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 这是被包含的Servlet
* @author Lzj
*
*/
public class IncludedServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 被包含的Servlet程序不能改变响应消息的状态码和响应头,下面两条语句不起作用
response.setContentType("text/html;charset=GB2312");
response.setCharacterEncoding("GB2312");
PrintWriter out = response.getWriter();
out.println("中国" + "<br>");
out.println("URI:" + request.getRequestURI() + "<br>");
out.println("QueryString:" + request.getQueryString() + "<br>"); // 请求消息请求行的资源路径并没有参数,所以为空
out.println("parameter_p1:" + request.getParameter("p1") + "<br>");
//out.println("id:" + request.getParameter("id") + "<br>");
}
}
访问包含其他资源的Servlet,http://localhost:8888/requestDispatcherInclude/servlet/IncludingServlet
虽然调用者在调用IncludedServlet时向它传递了一个参数,但是转发的请求消息并没有变,所以QueryString为空。
乱码是因为IncludingServlet程序的文本编码是GBK,getParameter默认按ISO8859-1编码获取数据,又因为p1不是浏览器提交的参数,所以通过request.setCharacterEncoding("GB2312");可解决乱码问题。
把IncludedServlet程序中最后一条语句注释去掉,重新访问http://localhost:8888/requestDispatcherInclude/servlet/IncludingServlet?id=1,注意添加了id参数
(2)被包含的资源是HTML文件
A、IncludingServlet中的response对象并未调用getWriter方法
IncludingServlet.java
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 这是包含其他资源的Servlet
* @author Lzj
*
*/
public class IncludingServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//response.setContentType("text/html; charset=GB2312");
RequestDispatcher rd = getServletContext().getRequestDispatcher
("/test.html");
rd.include(request, response);
}
}
test.html
<!DOCTYPE html>
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<h1>中文测试页面</h1>
</body>
</html>
访问包含其他资源的Servlet,http://localhost:8888/requestDispatcherInclude/servlet/IncludingServlet
凡是在Web.xml文件中找不到匹配<servlet-mapping>元素的URL访问请求,也就是所有其他Servlet都不处理的访问请求,都将交给Tomcat中设置的一个缺省Servlet处理,客户端对静态HTML文件和图片的访问其实都是由缺省的Servlet来完成响应的,所以,调用静态HTML文件与调用一个Servlet程序本质上没有什么区别。
Tomcat的缺省Servlet首先检查当前HttpServletResponse对象是否已经调用了getWriter方法返回过PrintWriter对象,如果已经调用则使用getWriter方法返回的PrintWriter对象来输出静态HTML文件中的内容,否则,它将调用getOutputStream方法返回的ServletOutputStream对象将静态HTML文件中的内容按字节流原封不动地写入到客户端。
因为test.hmtl文本编码与浏览器默认编码GBK(可在“设置_高级设置_自定义字体”查看)相同(但浏览器默认编码即便不是GBK,也可在Servlet程序调用setContentType方法来告诉浏览器用GBK显示),所以访问不会出现乱码。
B、IncludingServlet中的response对象调用getWriter方法
IncludingServlet.java
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 这是包含其他资源的Servlet
* @author Lzj
*
*/
public class IncludingServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
RequestDispatcher rd = getServletContext().getRequestDispatcher
("/test.html");
rd.include(request, response);
}
}
访问包含其他资源的Servlet,http://localhost:8888/requestDispatcherInclude/servlet/IncludingServlet
因为调用者IncludingServlet已经调用过当前response对象的getWriter方法,所以Tomcat缺省Servlet将使用getWriter方法返回PrintWriter对象来输出静态HTML文件中的内容。但由于IncludingServlet在调用getWriter方法之前,没有通过任何方式指定它所获得的PrintWriter对象所采用的字符集编码,该PrintWriter对象将test.html文件中的内容按默认的ISO8859-1编码输出到客户端,因为ISO8859-1编码不支持中文,所以出现乱码。
另外还需注意,如果把PrintWriter out = response.getWriter();语句挪到rd.include(request, response);语句后面,访问时会报错,因为在引入test.html文件之前response没调用getWriter方法,Tomcat缺省Servlet则调用getOutputStream方法。两个方法互相排斥,会报错。
三、用forward方法实现请求转发
1、forward方法
该方法用于将请求转发到RequestDispatcher对象封装的资源,Servlet程序在调用这个方法进行转发之前可以对请求进行一些前期的预处理。在MVC架构中,forward方法是一个核心方法,控制器组件(Controller)就是使用该方法来跳转到视图组件(Viewer),让视图组件产生响应内容返回给浏览器显示。
2、应用细节
A、
ForwardTestServlet.java(注释1、2、3、4为注意事项)
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ForwardTestServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1、在调用者程序和被调用者中设置响应状态码和响应头都不会被忽略
response.setContentType("text/hmtl; GB2312");
PrintWriter out = response.getWriter();
// 2、不显示,因为调用forward之前写入到缓冲区,缓冲区内容被清空
out.println("before forward!");
// 4、用于将缓冲区的内容强制刷新到客户端,但后面语句将不起作用
//response.flushBuffer();
RequestDispatcher rd = getServletContext().getRequestDispatcher("/test.html");
rd.forward(request, response);
// 3、不显示,因为调用forward之后写入到缓冲区,写入操作被忽略
out.println("after forward!");
}
}
访问包含资源的Servlet,http://localhost:8888/requestDispatcherInclude/servlet/IncludingServlet
将response.flushBuffer()的注释去掉,再次访问,可以发现在response.flushBuffer语句之前,输出的内容可以显示
B、
test.html(注意链接有id参数,且值为中文)
<!DOCTYPE html>
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<a href="servlet/ForwardingServlet?id=中国">访问包含其他资源的Servlet</a>
</body>
</html>
ForwardingServlet.java
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ForwardingServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String china = "中国";
// 路径必须以"/"开头,表示相对于Web应用的根目录(虚拟目录)
RequestDispatcher rd = getServletContext().getRequestDispatcher
("/servlet/ForwardedServlet?p1=" + china);
rd.forward(request, response);
}
}
ForwardedServlet.java
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ForwardedServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html; charset=GB2312");
PrintWriter out = response.getWriter();
request.setCharacterEncoding("GB2312");
out.println("URI:" + request.getRequestURI() + "<br>");
out.println("QueryString:" + request.getQueryString() + "<br>");
out.println("URL:" + request.getRequestURL() + "<br>");
out.println("id:" + request.getParameter("id") + "<br>");
out.println("p1:" + request.getParameter("p1"));
}
}
输入http://localhost:8888/requestDispatcherForward/test.html,访问test.html
点击超链接
与include方法对比,不难发现URI变为被调用者的URI,即Servlet容器将根据目标资源路径对当前request对象中的请求路径和参数信息进行调整,所以QueryString不为空
test.html的id参数是通过Get方式提交的参数,因此调用request.setCharacterEncoding方法并不能解决乱码问题。可以通过下面代码解决
String id = new String(request.getParameter("id").getBytes("ISO8859-1"), "GB2312");
out.println("id:" + id + "<br>");
因为p1不是浏览器提交的参数,而ForwardingServlet程序文本编码是GBK,所以调用request.setCharacterEncoding("GB2312");依然可解决乱码问题。
3、forward方法的相对路径问题
使用<base>标签指定基准地址
如:
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getRequestURI();
out.println("<base href=\"" + basePath + "\">");
来源:oschina
链接:https://my.oschina.net/u/1171518/blog/221742