本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。

请求分发

Servlet 的 RequestDispatcher 接口定义:

package javax.servlet;

import java.io.IOException;

public interface RequestDispatcher {
    String FORWARD_REQUEST_URI = "javax.servlet.forward.request_uri";
    String FORWARD_CONTEXT_PATH = "javax.servlet.forward.context_path";
    String FORWARD_PATH_INFO = "javax.servlet.forward.path_info";
    String FORWARD_SERVLET_PATH = "javax.servlet.forward.servlet_path";
    String FORWARD_QUERY_STRING = "javax.servlet.forward.query_string";
    String INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
    String INCLUDE_CONTEXT_PATH = "javax.servlet.include.context_path";
    String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
    String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
    String INCLUDE_QUERY_STRING = "javax.servlet.include.query_string";
    String ERROR_EXCEPTION = "javax.servlet.error.exception";
    String ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type";
    String ERROR_MESSAGE = "javax.servlet.error.message";
    String ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
    String ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name";
    String ERROR_STATUS_CODE = "javax.servlet.error.status_code";

    void forward(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    void include(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
}

它有 2 个方法,forward 和 include:

  1. public void forward(ServletRequest request,ServletResponse response)throws ServletException,java.io.IOException: Forwards a request from a servlet to another resource (servlet, JSP file, or HTML file) on the server.
  2. public void include(ServletRequest request,ServletResponse response)throws ServletException,java.io.IOException: Includes the content of a resource (servlet, JSP page, or HTML file) in the response.

forward 方法将请求从一个 Servlet 转发到另一个资源(Servlet、JSP 页面或 HTML 文件)。转发后,客户端的 URL 不会改变,且请求和响应对象在转发过程中保持不变。使用场景:

  • 在服务器内部资源之间传递请求
  • 需要在处理请求时共享相同的请求和响应对象

include 方法将另一个资源的内容包含到当前响应中。与 forward 不同,include 不会终止当前 Servlet 的执行,而是将目标资源的输出包含在当前响应中。使用场景:

  • 在生成响应时需要包含其他资源的内容
  • 需要在一个页面中动态地插入其他资源的内容

代码实践

通过登录页面,输入用户名和密码,如果密码是 servlet,forward 到 welcome servlet,否则 include 到登录页面,展示错误信息。

login.html 获取用户输入

<form action="go" method="get">
Name:<input type="text" name="userName"/><br/>
Password:<input type="password" name="userPass"/><br/>
<input type="submit" value="login"/>
</form>

Simple.java Servlet 类,如果密码是 servlet,forward 到 welcome servlet

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


public class Simple extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        String p = request.getParameter("userPass");
        if (p.equals("servlet")) {
            RequestDispatcher rd = request.getRequestDispatcher("welcome");
            rd.forward(request, response);
        } else {
            out.print("Sorry username or password error!");
            RequestDispatcher rd = request.getRequestDispatcher("login.html");
            rd.include(request, response);
        }
    }
}

WelcomeServlet.java Servlet 类,返回 welcome 信息

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


public class WelcomeServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        String n = request.getParameter("userName");
        out.print("Welcome " + n);
    }
}

web.xml 部署描述文件,servlet 信息

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <servlet>
        <description>This is the description of my J2EE component</description>
        <display-name>This is the display name of my J2EE component</display-name>
        <servlet-name>Simple</servlet-name>
        <servlet-class>Simple</servlet-class>
    </servlet>
    <servlet>
        <description>This is the description of my J2EE component</description>
        <display-name>This is the display name of my J2EE component</display-name>
        <servlet-name>WelcomeServlet</servlet-name>
        <servlet-class>WelcomeServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Simple</servlet-name>
        <url-pattern>/go</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>WelcomeServlet</servlet-name>
        <url-pattern>/welcome</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>login.html</welcome-file>
    </welcome-file-list>
</web-app>

web.xml 在低版本 Servlet 是必须的,在高版本 Servlet 已经不再需要。

注意 Simple.java 的 else 逻辑:

out.print("Sorry username or password error!");
RequestDispatcher rd = request.getRequestDispatcher("login.html");
rd.include(request, response);

如果把这里的 include 改为 forward,会发生什么?输出内容不会显示,因为 forward 方法会将控制权转交给 login.html,并且 login.html 的内容将替代当前响应内容。

总结,用 include 方法时,当前 Servlet 的输出内容会保留,并且目标资源的内容会被包含在当前响应中。使用 forward 方法时,当前 Servlet 的执行将终止,控制权将交给目标资源,目标资源的内容将替代当前响应内容。

重定向

我们在浏览器看到的 302,就是重定向。通常在重定向后,会自动发起新的 HTTP 请求。

HttpServletResponse 接口的 sendRedirect 方法:

void sendRedirect(String var1) throws IOException;

示例:

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MySearcher extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String name = request.getParameter("name");
        response.sendRedirect("https://www.google.co.in/#q=" + name);
    }
}  

forward 和 sendRedirect 的区别如下:

  • forward 是在服务端,sendRedirect 是在客户端

  • forward 发送相同的 request 和 response 对象给另一个 servlet,sendRedirect 总是生成新的 request 和 response 对象

  • forward 只能在服务器内部,sendRedirect 可以跨不同 Web 应用或服务器

总结,forward 适用于服务器内部资源之间的请求转发,效率较高,客户端 URL 不变。sendRedirect 适用于通知客户端资源位置改变或跨 Web 应用/服务器的请求转发,客户端 URL 会改变,有额外的网络开销。

参考资料:

https://www.javatpoint.com/requestdispatcher-in-servlet

https://www.javatpoint.com/sendRedirect()-method

https://www.liaoxuefeng.com/wiki/1252599548343744/1328761739935778