首页 > 编程笔记 > Java笔记 阅读:38

Servlet HttpServletRequest和HttpServletResponse的用法(非常详细)

Servlet 处理请求和做出响应主要用到两个重要接口,一个是  HttpServletRequest,用来处理请求;另一个是 HttpServletResponse,用来处理响应。

HttpServletRequest处理请求

HttpServletRequest 接口是 ServletRequest 接口的子接口,封装了 HTTP 请求的相关信息。浏览器请求服务器端时,会封装请求报文交给服务器端,服务器端接收到请求后,将请求报文解析生成 HttpServletRequest 接口的实现类对象,简称 HttpServletRequest 对象。

HttpServletRequest 对象由 Servlet 容器创建,同时将传入 HttpServlet 类的 doGet(HttpServletRequest req, HttpServletResponse res) 或者 doPost(HttpServletRequest req, HttpServletResponse res) 方法中。

HttpServletRequest 接口包含的方法,如下图所示:


图 1 HttpServletRequest接口包含的方法

通过 HttpServletRequest 可以从请求报文中获取数据,比如获取 URL 地址参数,包括 IP 地址、端口号、协议、上下文路径等,以及获取请求头信息、请求参数等。

1) 获取 URL 地址参数

比如获取主机名、端口号、协议,以及上下文路径,示例代码如下:
// 获取上下文路径(重要)
String path = request.getContextPath();
System.out.println("上下文路径:" + path);
// 获取主机名
String serverName = request.getServerName();
System.out.println("主机名:" + serverName);
// 获取端口号
int serverPort = request.getServerPort();
System.out.println("端口号:" + serverPort);
// 获取协议
String scheme = request.getScheme();
System.out.println("协议:" + scheme);

2) 获取请求头信息

比如获取 User-Agent 和 Referer 信息,示例代码如下:
// 获取 User-Agent 信息
String header = request.getHeader("User-Agent");
System.out.println("user-agent:" + header);
// 获取 Referer 信息
String referer = request.getHeader("Referer");
System.out.println("上个页面的地址:" + referer); // 如登录失败,返回登录页面让用户继续登录

3) 获取请求方式

目前请求方式只有 GET 和 POST,示例代码如下:
String method = request.getMethod();
System.out.println("method = " + method);

4) 获取请求参数

请求参数就是浏览器向服务器端提交的数据。那么,浏览器如何向服务器端发送数据呢?

对于 GET 请求,请求参数通常拼接在 URL 后面。而对于 POST 请求,请求参数通常会放到请求体中。例如,使用表单进行请求参数提交:
<!-- action 属性:Servlet 的映射路径
     method 属性:请求的提交方式,get 或 post 均可
     注意:post 请求测试完后可以将 method 的属性值修改为 get 再次进行测试 -->
<form action="paramTestServlet" method="post">
    姓名:<input type="text" name="name"/><br />
    手机号码:<input type="text" name="phone"/><br />
    你喜欢的足球队<br />
    巴西<input type="checkbox" name="soccerTeam" value="Brazil" />
    德国<input type="checkbox" name="soccerTeam" value="German" />
    荷兰<input type="checkbox" name="soccerTeam" value="Holland" />
    <input type="submit" value="提交" />
</form>

使用 HttpServletRequest 对象获取请求参数,借助 getParameter() 和 getParameterValues(String name) 方法实现,示例代码如下:
// 一个参数对应一个值的情况
String name = request.getParameter("name");
System.out.println("name=" + name);
String phone = request.getParameter("phone");
System.out.println("phone=" + phone);

// 一个参数对应一组值的情况
String[] soccerTeams = request.getParameterValues("soccerTeam");
for (int i = 0; i < soccerTeams.length; i++) {
    System.out.println("team " + i + "=" + soccerTeams[i]);
}

5) 请求的转发

转发是进行页面跳转的一种方式,可以从一个 Servlet 跳转至另一个 Servlet,也可以跳转至其他页面。
// 1. 获取请求转发对象
RequestDispatcher dispatcher = request.getRequestDispatcher("转发目的地路径");
// 2. 发起转发
dispatcher.forward(request, response);
例如,创建 FirstServlet 类实现将请求转发至 SecondServlet 类,示例代码如下:
package com.atguigu.servlet;
//省略 import 语句

@WebServlet("/first")
public class FirstServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("访问到了 FirstServlet...");

        // 1. 获取请求转发对象
        // RequestDispatcher dispatcher = request.getRequestDispatcher("second");
        // 2. 发起转发
        // dispatcher.forward(request, response);
        // 或者以上两步合为一步
        request.getRequestDispatcher("second").forward(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

例如,创建 SecondServlet 类将请求转发至 success.html 页面,示例代码如下:
package com.atguigu.servlet;
//省略 import 语句

public class SecondServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("访问到了 SecondServlet...");
        // 转发到 success.html, getRequestDispatcher 方法的实参,填写 success.html 的路径
        request.getRequestDispatcher("success.html").forward(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

6) HttpServletRequest 对象作为请求域对象共享数据

HttpServletRequest 对象可以作为域对象,称为请求域对象,它的作用范围为一次请求,所有数据在本次请求内有效,一旦本次请求结束,请求中的所有数据也就消失了。

值得注意的是,请求的转发发生在一次请求内。例如,FirstServlet 共享 HttpServletRequest 对象中的数据到 SecondServlet 或者 success.html,必须是转发的关系。

操作域对象中数据的常用方法如下:
示例代码如下:
// 1. 在 FirstServlet 中将数据保存到 HttpServletRequest 对象的属性域中
request.setAttribute("attrName", "attrValueInRequest");

// 2. 共享 HttpServletRequest 对象中的数据,必须是转发的关系
request.getRequestDispatcher("second").forward(request, response);

// 3. 在 SecondServlet 中从 HttpServletRequest 对象的属性域中获取数据
Object attribute = request.getAttribute("attrName");
System.out.println("attrValue=" + attribute);

下面通过一个表单提交功能来练习 HttpServletRequest 对象的使用。

创建一个表单,内容包括 username、password、email 和 hobby 等信息,实现单击“提交”按钮,将表单数据提交到后端 Servlet 进行处理,并封装所有数据到一个 user 对象。

首先在 index.html 页面中,编写代码填写如下表单信息:
<form action="requestTest" method="post">
    username:<input type="text" name="username" /><br />
    password:<input type="password" name="password" /><br />
    email:<input type="text" name="email" /><br />
    hobby:<input type="checkbox" name="hobby" value="Java" />Java
    <input type="checkbox" name="hobby" value="MySQL" />MySQL
    <input type="checkbox" name="hobby" value="Python" />Python<br />
    <input type="submit" value="提交" /><br /><br />
</form>

创建 User 类对应表单信息,示例代码如下:
package com.atguigu.bean;
import java.util.Arrays;

public class User {
    private Integer id;
    private String username;
    private String password;
    private String email;
    private String[] hobby;

    // 省略 get 和 set、构造器等方法
}

创建 RequestTestServlet 类处理表单,实现将其数据加入 user 对象中,示例代码如下:
package com.atguigu.servlet;
//省略 import 语句

@WebServlet("/requestTest")
public class RequestTestServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("访问到了 doGet 方法...");
        // 获取表单提交的数据
        String username = request.getParameter("username");
        System.out.println("username = " + username);

        String password = request.getParameter("password");
        System.out.println("password = " + password);

        String email = request.getParameter("email");
        System.out.println("email = " + email);

        String[] hobbies = request.getParameterValues("hobby");
        System.out.println(Arrays.toString(hobbies));

        // 实例化一个 User 对象,将数据注入到 User 对象内
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        user.setEmail(email);
        user.setHobby(hobbies);
        System.out.println("user = " + user);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("访问到了 doPost 方法...");
        this.doGet(request, response);
    }
}

启动项目填写表单如下图所示:


图 2 提交表单后查看控制台

然后提交数据后查看控制台,如下图所示:


图 3 提交表单后查看控制台

由于 id 没有传值,结果为 null,其余参数都成功赋值给 user 对象。

另外,获取参数还有一种更简便的方式,即借助 getParameterMap() 方法,将所有请求参数存储在 map 集合中。
/**
* 获取的 map 集合 key 值存储的是请求参数的 key 值,value 值存储的请求参数的 value 值,
* 有可能有一个 key 对应一组 value 的情况,因此 value 值的数据类型是 String 的数组类型
*/
Map<String, String[]> map = request.getParameterMap();
// 获取 map 集合内的数据
// 1. 获取所有的 key 值
Set<String> strings = map.keySet();
for (String string : strings) {
    System.out.println("string = " + string);
}
System.out.println("----------------------------");
// 2. 获取所有的 value 值
Collection<String[]> values = map.values();
for (String[] value : values) {
    System.out.println(Arrays.toString(value));
}
System.out.println("----------------------------");
// 3. 获取所有的键值对
Set<Map.Entry<String, String[]>> entries = map.entrySet();
for (Map.Entry<String, String[]> entry : entries) {
    System.out.println("entry = " + entry);
}
传统方式下,map 集合中的数据是按照上述代码依次遍历的,但相对来说过于烦琐,为此引入第三方工具类 BeanUtils,自动将 map 集合中的数据映射到对应的 JavaBean 对象中。不过前提条件是,必须保证 map 集合的 key 值和 JavaBean 对象的属性名一一对应。

使用第三方工具类 BeanUtils,首先需要引入其 JAR 包,并放入到 web/WEB-INF/lib 目录下,如下图所示:


图 4 引入BeanUtils的相关JAR包

单击右键,将所有 JAR 包添加为“Add as Library”,这样才能保证 JAR 包起作用,如下图所示:


图 5 将JAR包添加为“Add as Library”

然后修改 RequestTestServlet 的 doGet() 方法,借助 BeanUtils 工具类重新实现将请求参数保存到 user 对象中,示例代码如下:
package com.atguigu.servlet;
//省略 import 语句

public class RequestTestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("访问到了 doGet 方法...");

        // 获取所有的请求参数,存储在 map 集合内
        Map<String, String[]> map = request.getParameterMap();

        // 实例化一个 User 对象
        User user = new User();
        try {
            // 使用 BeanUtils 中的静态方法 populate,将 map 集合内的数据注入到 user 对象内
            BeanUtils.populate(user, map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 展示结果
        System.out.println("user = " + user);
    }
}
启动项目,再次填写表单后查看控制台,如下图所示:


图 6 填写表单后再次查看控制台

发现,同样实现将请求参数成功保存到user对象中。

HttpServletResponse处理响应

HttpServletResponse 接口是 ServletResponse 接口的子接口,封装了服务器端针对 HTTP 响应的相关信息。

HttpServletResponse 接口的实体类对象,简称 HttpServletResponse 对象,同样是由 Servlet 容器创建,并传入 HttpServlet 类的 service(HttpServletRequest req, HttpServletResponse res) 方法中。

HttpServletResponse 接口包含的方法如下图所示:


图 7 HttpServletResponse接口包含的方法

HttpServletResponse 对象的主要功能,具体如下。

1) 通过输出流方式向浏览器输出数据

通过输出流方式向浏览器输出数据,一般情况下,请求来自哪里就在哪里输出数据。值得注意的是,如果浏览器作为客户端,不会使用这种方式作为响应结果,而是采用转发或者重定向。
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 通过 PrintWriter 对象向浏览器端发送响应信息
    PrintWriter writer = response.getWriter();
    writer.write("Servlet response");
    // 关闭输出流
    writer.close();
}
其中,写出的数据可以是页面、页面片段、字符串等。当写出的数据包含中文时,浏览器接收到的响应数据就可能有乱码。

为了避免乱码,可以使用 HttpServletResponse 对象在向浏览器输出数据前设置响应头。响应头就是浏览器解析页面的配置。

2) 设置响应头信息

例如,告诉浏览器使用哪种编码和文件格式解析响应体内容,示例代码如下:
//response.setHeader("Content-Type", "text/html;charset=UTF-8");
//或简写方式
response.setContentType("text/html;charset=UTF-8");
设置好以后,在浏览器的响应报文中可以查看到设置的响应头中的信息。如下图所示:


图 8 查看响应头信息

3) 重定向

重定向和转发类似,也是进行页面跳转的一种方式,使用重定向同样可以实现从一个 Servlet 跳转到另一个 Servlet,也可以跳转到其他页面。
response.sendRedirect("重定向目的地路径");

例如,将请求重定向到 SecondServlet 页面,示例代码如下:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 调用 HttpServletResponse 对象的 sendRedirect() 方法,传入的参数是 success.html 路径
    response.sendRedirect("success.html");
}
注意路径问题,加上“/”会导致失败,因为转发以“/”开始表示项目根路径,重定向以“/”开始表示主机地址,而重定向一般需要加上项目名。

转发和重定向的区别

请求的转发与重定向都是 Web 应用页面跳转的主要手段,在 Web 应用中使用非常广泛。我们一定要搞清楚两者的区别,如下图所示。


图 9 转发和重定向的区别

对于转发来说,图 9 中第一个 Servlet 接收到了浏览器端的请求,进行了一定的处理,然后没有立即对请求进行响应,而是将请求“交给下一个 Servlet”继续处理,下一个 Servlet 处理完成后,对浏览器进行了响应。

在服务器端内部将请求“交给”其他组件继续处理称为请求的转发。对浏览器来说,一共只发了一次请求,服务器端内部进行的“转发”浏览器感觉不到,同时浏览器地址栏中的地址不会变成“下一个Servlet”的虚拟路径。请求转发的过程如下图所示。


图 10 请求转发的过程

综上所述,在转发的情况下,两个 Servlet 可以共享同一个 HttpServletRequest 对象中保存的数据,还可以直接访问 WEB-INF 下的资源。

而对于重定向而言,图 9 中 Servlet1 接收到了浏览器端的请求,进行了一定的处理,然后给浏览器一个特殊的响应消息,这个特殊的响应消息会通知浏览器去访问另外一个资源,这个动作是服务器和浏览器自动完成的。整个过程中浏览器端会发出两次请求,且在浏览器地址栏里面能够看到地址的改变,改变为下一个资源的地址。

请求重定向的过程如下图所示:


图 11 请求重定向的过程

重定向的情况下,原 Servlet 和目标资源之间就不能共享请求域数据了。请求转发和请求重定向的区别如下表所示。

表:请求转发和请求重定向的区别
  转发 重定向
浏览器感知 在服务器端内部完成,浏览器感知不到 服务器端以 302 状态码通知浏览器访问新地址,浏览器有感知
浏览器地址栏 不改变 改变
整个过程发送请求次数 一次 两次
能否共享 HttpServletRequest 对象数据 不能
WEB-INF 下的资源 能够访问 不能访问
目标资源 必须是当前 Web 应用中的资源 不局限于当前 Web 应用

值得重点关注的是,浏览器不能访问服务器端 WEB-INF 下的资源,而服务器端是可以访问的。因此转发可以转发到 WEB-INF 下的资源,但重定向是不能重定向到 WEB-INF 下的资源的。

相关文章