Servlet HttpServletRequest和HttpServletResponse的用法(非常详细)
Servlet 处理请求和做出响应主要用到两个重要接口,一个是 HttpServletRequest,用来处理请求;另一个是 HttpServletResponse,用来处理响应。
HttpServletRequest 对象由 Servlet 容器创建,同时将传入 HttpServlet 类的 doGet(HttpServletRequest req, HttpServletResponse res) 或者 doPost(HttpServletRequest req, HttpServletResponse res) 方法中。
HttpServletRequest 接口包含的方法,如下图所示:

图 1 HttpServletRequest接口包含的方法
通过 HttpServletRequest 可以从请求报文中获取数据,比如获取 URL 地址参数,包括 IP 地址、端口号、协议、上下文路径等,以及获取请求头信息、请求参数等。
对于 GET 请求,请求参数通常拼接在 URL 后面。而对于 POST 请求,请求参数通常会放到请求体中。例如,使用表单进行请求参数提交:
使用 HttpServletRequest 对象获取请求参数,借助 getParameter() 和 getParameterValues(String name) 方法实现,示例代码如下:
例如,创建 SecondServlet 类将请求转发至 success.html 页面,示例代码如下:
值得注意的是,请求的转发发生在一次请求内。例如,FirstServlet 共享 HttpServletRequest 对象中的数据到 SecondServlet 或者 success.html,必须是转发的关系。
操作域对象中数据的常用方法如下:
示例代码如下:
下面通过一个表单提交功能来练习 HttpServletRequest 对象的使用。
创建一个表单,内容包括 username、password、email 和 hobby 等信息,实现单击“提交”按钮,将表单数据提交到后端 Servlet 进行处理,并封装所有数据到一个 user 对象。
首先在 index.html 页面中,编写代码填写如下表单信息:
创建 User 类对应表单信息,示例代码如下:
创建 RequestTestServlet 类处理表单,实现将其数据加入 user 对象中,示例代码如下:
启动项目填写表单如下图所示:

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

图 3 提交表单后查看控制台
由于 id 没有传值,结果为 null,其余参数都成功赋值给 user 对象。
另外,获取参数还有一种更简便的方式,即借助 getParameterMap() 方法,将所有请求参数存储在 map 集合中。
使用第三方工具类 BeanUtils,首先需要引入其 JAR 包,并放入到 web/WEB-INF/lib 目录下,如下图所示:

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

图 5 将JAR包添加为“Add as Library”
然后修改 RequestTestServlet 的 doGet() 方法,借助 BeanUtils 工具类重新实现将请求参数保存到 user 对象中,示例代码如下:

图 6 填写表单后再次查看控制台
发现,同样实现将请求参数成功保存到user对象中。
HttpServletResponse 接口的实体类对象,简称 HttpServletResponse 对象,同样是由 Servlet 容器创建,并传入 HttpServlet 类的 service(HttpServletRequest req, HttpServletResponse res) 方法中。
HttpServletResponse 接口包含的方法如下图所示:

图 7 HttpServletResponse接口包含的方法
HttpServletResponse 对象的主要功能,具体如下。
为了避免乱码,可以使用 HttpServletResponse 对象在向浏览器输出数据前设置响应头。响应头就是浏览器解析页面的配置。

图 8 查看响应头信息
response.sendRedirect("重定向目的地路径");
例如,将请求重定向到 SecondServlet 页面,示例代码如下:

图 9 转发和重定向的区别
对于转发来说,图 9 中第一个 Servlet 接收到了浏览器端的请求,进行了一定的处理,然后没有立即对请求进行响应,而是将请求“交给下一个 Servlet”继续处理,下一个 Servlet 处理完成后,对浏览器进行了响应。
在服务器端内部将请求“交给”其他组件继续处理称为请求的转发。对浏览器来说,一共只发了一次请求,服务器端内部进行的“转发”浏览器感觉不到,同时浏览器地址栏中的地址不会变成“下一个Servlet”的虚拟路径。请求转发的过程如下图所示。

图 10 请求转发的过程
综上所述,在转发的情况下,两个 Servlet 可以共享同一个 HttpServletRequest 对象中保存的数据,还可以直接访问 WEB-INF 下的资源。
而对于重定向而言,图 9 中 Servlet1 接收到了浏览器端的请求,进行了一定的处理,然后给浏览器一个特殊的响应消息,这个特殊的响应消息会通知浏览器去访问另外一个资源,这个动作是服务器和浏览器自动完成的。整个过程中浏览器端会发出两次请求,且在浏览器地址栏里面能够看到地址的改变,改变为下一个资源的地址。
请求重定向的过程如下图所示:

图 11 请求重定向的过程
重定向的情况下,原 Servlet 和目标资源之间就不能共享请求域数据了。请求转发和请求重定向的区别如下表所示。
值得重点关注的是,浏览器不能访问服务器端 WEB-INF 下的资源,而服务器端是可以访问的。因此转发可以转发到 WEB-INF 下的资源,但重定向是不能重定向到 WEB-INF 下的资源的。
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,必须是转发的关系。
操作域对象中数据的常用方法如下:
- setAttribute(String s, Object o),表示将数据以键值对的形式共享到域对象内;
- getAttribute(String s),表示从域对象内根据 key 值获取 value 值;
- removeAttribute(String s),表示从域对象内根据 key 值移除共享数据。该方法在 request 对象上使用概率很小,因为请求域中数据的时效性很短。
示例代码如下:
// 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 下的资源的。