一、Tomcat 请求响应机制
1.1 Request 和 Response 对象的创建
当 Web 服务器(如 Tomcat)接收到客户端的 HTTP 请求时,会针对每一次请求分别创建两个对象:
HttpServletRequest request:代表请求对象,封装了客户端发送给服务器的所有请求数据(请求行、请求头、请求体等)。
HttpServletResponse response:代表响应对象,封装了服务器要返回给客户端的所有响应数据(状态码、响应头、响应体等)。
这两个对象由 Tomcat 创建,并作为参数传递给 Servlet 的 service() 方法(以及 doGet()、doPost() 等方法)。开发者可以直接使用它们来获取请求数据和设置响应数据。
1.2 Request 和 Response 的作用
Request 和 Response 是 Servlet 开发中最核心的两个对象,它们的作用可以概括为:
Request 的作用:
获取客户端提交的请求参数(表单数据、URL 参数等)。
获取请求头信息(User-Agent、Referer、Cookie 等)。
获取客户端信息(IP 地址、端口号等)。
作为域对象在请求转发过程中共享数据。
Response 的作用:
向客户端输出响应内容(HTML、JSON、文本等)。
设置响应状态码(200、404、500 等)。
设置响应头信息(Content-Type、Refresh、Location 等)。
实现重定向。
二、HttpServletRequest 基本功能
2.1 Request 的四大功能
HttpServletRequest 在 JavaWeb 开发中极为重要,它是 Servlet 中处理请求的核心对象。它的功能可以归纳为以下四类:
功能一:封装了请求头数据
可以获取浏览器发送的所有 HTTP 请求头信息。
功能二:封装了请求正文数据
对于 POST 请求,可以获取请求体中的参数数据。GET 请求没有请求体。
功能三:是一个域对象
可以像 Map 一样存储和获取数据,在请求转发过程中共享数据。
功能四:实现请求转发
可以将请求转发到另一个 Servlet、JSP 或 HTML 页面,实现服务器内部的跳转。
2.2 获取请求头数据
通过 Request 对象可以获取客户端发送的所有请求头信息。
核心方法:
String getHeader(String name):获取指定名称的请求头值。
Enumeration getHeaderNames():获取所有请求头的名称集合。
代码示例:
response.setContentType("text/html;charset=utf-8");
Enumeration names = request.getHeaderNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
String value = request.getHeader(name);
System.out.println(name + ": " + value);
response.getWriter().println(name + ": " + value + "<br/>");
}
常见请求头说明:
User-Agent:浏览器类型和版本信息。
Referer:请求来源页面地址。
Cookie:客户端保存的 Cookie 数据。
Host:请求的目标主机和端口。
Accept-Language:客户端支持的语言。
2.3 获取请求相关的其他方法
Request 对象提供了大量与请求相关的方法,下面按功能分类列出:
与请求 URL 相关的方法:
String getContextPath():获取上下文路径(项目名称),例如 /demo。
String getRequestURI():获取请求 URI 路径,例如 /demo/login。
StringBuffer getRequestURL():获取完整的请求 URL,例如 http://localhost:8080/demo/login。
String getServletPath():获取 Servlet 映射路径,例如 /login。
String getQueryString():获取 URL 中的参数字符串,例如 name=zhangsan&age=18。
与请求方法相关的方法:
String getMethod():获取请求方法,例如 GET、POST、PUT、DELETE。
与客户端信息相关的方法:
String getRemoteAddr():获取客户端 IP 地址。
String getRemoteHost():获取客户端主机名。
int getRemotePort():获取客户端端口号。
与服务器信息相关的方法:
String getScheme():获取请求协议,例如 http 或 https。
String getServerName():获取服务器主机名。
int getServerPort():获取服务器端口号。
与请求编码相关的方法:
String getCharacterEncoding():获取请求编码,默认返回 null(表示 ISO-8859-1)。
void setCharacterEncoding(String code):设置请求编码,只对 POST 请求的正文有效。
与请求内容相关的方法:
int getContentLength():获取请求正文的字节数,GET 请求返回 -1。
String getContentType():获取请求内容类型,POST 默认为 application/x-www-form-urlencoded。
Locale getLocale():获取客户端浏览器支持的语言和地区。
三、HttpServletRequest 获取参数
3.1 传递参数的两种方式:GET 和 POST
在 Web 开发中,客户端向服务器传递参数主要有两种方式:
GET 方式传参(参数在 URL 中可见):
方式一:地址栏直接拼接参数
http://localhost/param/ParamServlet?p1=v1\&p2=v2
方式二:超链接携带参数
<a href="http://localhost/param/ParamServlet?p1=v1\&p2=v2">点击</a>
方式三:表单 method="GET"
<form method="GET" action="ParamServlet">...</form>
POST 方式传参(参数在请求体中,不可见):
方式一:表单 method="POST"
<form method="POST" action="ParamServlet">...</form>
3.2 获取参数的核心方法
Request 对象提供了以下四个核心方法来获取请求参数:
方法一:String getParameter(String name)
通过参数名获取单个参数值。适用于文本框、密码框、单选按钮、下拉框等单值控件。
方法二:String\[\] getParameterValues(String name)
通过参数名获取参数值数组。适用于复选框等可能传递多个值的控件。
方法三:Enumeration getParameterNames()
获取所有参数名的枚举集合。
方法四:Map<String, String\[\]> getParameterMap()
获取所有参数组成的 Map,键为参数名,值为参数值数组。
3.3 单值参数接收
单值参数包括:文本框、密码框、隐藏域、单选按钮、单值下拉框等。
前台页面示例(GET 和 POST 均可):
<form action="ParamServlet" method="post">
<input type="text" name="p1"/><br/>
<input type="text" name="p2"/><br/>
<input type="submit" value="提交"/><br/>
</form>
超链接传参:
<a href="ParamServlet?p1=v1&p2=v2">Param</a>
后台接收代码:
String s1 = request.getParameter("p1");
String s2 = request.getParameter("p2");
response.getWriter().print("p1 = " + s1 + "<br/>");
response.getWriter().print("p2 = " + s2 + "<br/>");
3.4 多值参数接收
复选框(checkbox)是最典型的多值参数,多个复选框可以使用相同的 name
3.5 获取所有参数的通用方式
使用 getParameterNames() 或 getParameterMap() 可以遍历所有参数,适合参数名未知或动态参数的场景。
方式一:使用 Enumeration 遍历
Enumeration names = request.getParameterNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
String value = request.getParameter(name);
System.out.println(name + " = " + value);
}
方式二:使用 Map 遍历
Map<String, String[]> map = request.getParameterMap();
Set<String> keySet = map.keySet();
for (String key : keySet) {
String[] vals = map.get(key);
System.out.println(key + " : " + Arrays.toString(vals));
}
四、中文乱码处理
4.1 POST 请求中文乱码解决方案
POST 请求的参数在请求体中传输,默认使用 ISO-8859-1 编码,导致中文乱码。有两种解决方案:
方案一:手动转码(通用,但较繁琐)
String name = request.getParameter("name");
// 先用 ISO-8859-1 获取字节数组,再用 UTF-8 重新构造字符串
name = new String(name.getBytes("ISO-8859-1"), "UTF-8");
System.out.println("name: " + name);
方案二:设置请求编码(推荐,简洁高效)
// 必须在调用 getParameter() 之前设置
request.setCharacterEncoding("UTF-8");
String name = request.getParameter("name");
System.out.println("name: " + name);
注意:request.setCharacterEncoding() 只对 POST 请求的请求体有效,对 GET 请求无效。
4.2 GET 请求中文乱码解决方案
GET 请求的参数在 URL 的 queryString 中,不存在请求体,所以 setCharacterEncoding() 无效。有两种解决方案:
方案一:手动转码(与 POST 相同)
String name = request.getParameter("name");
name = new String(name.getBytes("ISO-8859-1"), "UTF-8");
System.out.println("name: " + name);
方案二:修改 Tomcat 配置文件(推荐)
在 Tomcat 的 conf/server.xml 中,修改 Connector 配置,添加 URIEncoding 属性:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"/>
配置后,所有 GET 请求的参数都会使用 UTF-8 解码,无需再手动转码。
五、请求转发与 Request 域对象
5.1 请求转发(RequestDispatcher)
请求转发(Forward)是服务器内部的跳转方式,由 Request 对象发起。它发生在服务器内部,客户端浏览器并不知道发生了跳转,地址栏不会发生变化。
基本语法:
request.getRequestDispatcher("/目标路径").forward(request, response);
示例:跳转到 success.html
request.getRequestDispatcher("/success.html").forward(request, response);
示例:跳转到另一个 Servlet
request.getRequestDispatcher("/otherServlet").forward(request, response);
请求转发的特点:
服务器内部跳转,浏览器地址栏不变。
整个过程处于同一个请求中,Request 域对象中的数据可以共享。
只能跳转到当前 Web 应用内部的资源。
效率比重定向高(减少了客户端与服务器的一次往返)。
5.2 Request 域对象的作用范围
Request 是一个域对象,可以在请求转发过程中存储和传递数据。
存储数据:
request.setAttribute("name", "张三");
获取数据:
String name = (String) request.getAttribute("name");
Request 域对象的生命周期:
创建:客户端发送请求时,Tomcat 创建 Request 对象。
销毁:服务器处理完请求并返回响应后,Request 对象被销毁。
范围:仅在一次请求中有效,请求转发时数据可以传递到目标资源。
三大域对象范围对比(从小到大):
HttpServletRequest(请求域) < HttpSession(会话域) < ServletContext(应用域)
六、HttpServletResponse
6.1 Response 的四大功能
HttpServletResponse 是响应对象,用于向客户端输出数据。它的功能可以分为以下四类:
功能一:设置响应头信息
可以设置各种响应头,如 Content-Type、Refresh、Location 等。
功能二:发送状态码
可以设置响应状态码(200、404、500 等)。
功能三:设置响应正文
通过 getWriter() 或 getOutputStream() 向客户端输出内容。
功能四:实现重定向
通过 sendRedirect() 方法将客户端重定向到另一个资源。
6.2 设置状态码
Response 提供了设置和发送状态码的方法:
设置正常状态码:
response.setStatus(200); // 正常响应,通常无需手动设置
发送错误状态码:
response.sendError(404, "您要查找的资源不存在");
response.sendError(500, "服务器内部错误");
设置响应类型和编码:
// 设置响应类型为 HTML,编码为 UTF-8
response.setContentType("text/html;charset=utf-8");
// 单独设置编码(与上面配合使用)
response.setCharacterEncoding("UTF-8");
6.3 设置响应头信息
通过 Response 可以设置各种 HTTP 响应头:
设置 Content-Type:
response.setHeader("Content-Type", "text/html;charset=utf-8");
// 等价于 response.setContentType("text/html;charset=utf-8");
设置定时刷新(5秒后跳转到百度):
response.setHeader("Refresh", "5; URL=http://www.baidu.com");
设置禁用缓存:
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", -1);
6.4 Response 中文乱码处理
使用 Response 向客户端输出中文时,如果未设置正确的编码,会出现乱码。解决方法:
方法一:使用 setContentType(推荐)
// 同时设置响应类型和编码
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("张三");
方法二:分别设置
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Type", "text/html;charset=UTF-8");
response.getWriter().write("张三");
注意:setContentType 必须在 getWriter() 之前调用才有效。
七、重定向(Response 的核心功能)
7.1 重定向的基本使用
重定向(Redirect)是通过 Response 对象实现的一种页面跳转方式。它本质上是服务器向客户端发送一个 302 状态码和 Location 响应头,告诉浏览器重新向另一个地址发送请求。
基本语法:
response.sendRedirect("目标路径");
示例:
// 重定向到 success.html
response.sendRedirect("/demo/success.html");
// 重定向到另一个 Servlet
response.sendRedirect("/demo/otherServlet");
// 重定向到外部网站
response.sendRedirect("http://www.baidu.com");
7.2 重定向的特点
重定向具有以下特点:
客户端行为,浏览器地址栏会发生变化,显示新的 URL。
两次请求:第一次请求返回重定向响应,第二次请求访问目标资源。
不共享 Request 域数据:两次请求是独立的,Request 域中的数据无法传递。
可以跨域访问:可以重定向到外部网站(如百度)。
可以重定向到任意资源,不限于当前 Web 应用。