RequestDispatcher

1、RequestDispatcher

RequestDispatcher实例对象是由Servlet引擎建立的,它用于包装一个要被其余资源调用的资源,例如Servlet、HTML文件,JSP文件等,并能够经过其中的方法将客户端的请求转发给所包装的资源。RequestDispatcher接口定义了forward和include方法,forward用于将请求转发到RequestDispatcher对象封装的资源,include用于将RequestDispatcher对象封装的资源做为当前响应内容的一部门包含进来。html

ServletContext接口中定义了两个用于获取RequestDispatcher对象的方法:java

  • getRequestDispatcher方法:返回包装了某个路径所指定的资源的RequestDispatcher对象,传递给该方法的路径字符串必须以“/”开头,“/”表明当前Web应用程序的根目录(虚拟目录)。WEB-INF目录中的内容对RequestDispatcher对象是可见的,因此,传递给getRequestDispatcher方法的资源能够是WEB-INF目录中不能被外界访问的文件。浏览器

  • getNamedDispatcher方法:返回包装了某个Servlet或JSP文件的RequestDispatcher对象,传递给该方法的参数是在Web应用程序部署描述符中为Servlet或JSP文件指定的友好名称。架构

在ServletRequest接口中也定义了一个getRequestDispatcher方法,它与ServletContext.getRequestDispatcher的区别是:传递给这个方法的参数除了能够采用“/”开头的路径字符串,还能够采用非“/”开头的相对路径。app


2、用include方法实现资源包含

一、include方法

该方法用于将RequestDispatcher对象封装的资源做为当前响应内容的一部门包含进来,被包含的Servlet程序不能改变响应消息的状态码和响应头。注意一点是:URL路径一直是调用者程序的路径。ide

二、应用细节

(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/IncludingServletspa

  • 虽然调用者在调用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方法。两个方法互相排斥,会报错。


3、用forward方法实现请求转发

一、forward方法

该方法用于将请求转发到RequestDispatcher对象封装的资源,Servlet程序在调用这个方法进行转发以前能够对请求进行一些前期的预处理。在MVC架构中,forward方法是一个核心方法,控制器组件(Controller)就是使用该方法来跳转到视图组件(Viewer),让视图组件产生响应内容返回给浏览器显示。

二、应用细节

A、

ForwardTestServlet.java(注释一、二、三、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 {
        
        // 一、在调用者程序和被调用者中设置响应状态码和响应头都不会被忽略
        response.setContentType("text/hmtl; GB2312");
        PrintWriter out = response.getWriter();
    
        // 二、不显示,由于调用forward以前写入到缓冲区,缓冲区内容被清空
        out.println("before forward!"); 
    
        // 四、用于将缓冲区的内容强制刷新到客户端,但后面语句将不起做用
        //response.flushBuffer();
    
        RequestDispatcher rd = getServletContext().getRequestDispatcher("/test.html");
        rd.forward(request, response);

        // 三、不显示,由于调用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");依然可解决乱码问题。

三、forward方法的相对路径问题

使用<base>标签指定基准地址

如:

String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getRequestURI();
out.println("<base href=\"" + basePath + "\">");
相关文章
相关标签/搜索