第30课:请求与响应
本课目标:掌握HttpServletRequest和HttpServletResponse的使用,理解请求转发与重定向的区别。
一、概念讲解
1.1 请求与响应模型
┌─────────────────────────────────────────────────────────────────┐
│ HTTP请求 │
├─────────────┬─────────────┬─────────────┬───────────────────────┤
│ 请求行 │ 请求头 │ 空行 │ 请求体 │
│ GET /path │ Host: ... │ │ username=admin&... │
│ HTTP/1.1 │ User-Agent │ │ │
└─────────────┴─────────────┴─────────────┴───────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ HTTP响应 │
├─────────────┬─────────────┬─────────────┬───────────────────────┤
│ 状态行 │ 响应头 │ 空行 │ 响应体 │
│ HTTP/1.1 │ Content- │ │ <html>...</html> │
│ 200 OK │ Type: ... │ │ │
└─────────────┴─────────────┴─────────────┴───────────────────────┘
1.2 转发与重定向
转发 (Forward):
浏览器 -> Tomcat -> Servlet1 --forward--> Servlet2 -> Tomcat -> 浏览器
URL不变,服务器内部跳转
重定向 (Redirect):
浏览器 -> Tomcat -> Servlet1 --302--> 浏览器 -> 重新请求Servlet2
URL改变,客户端跳转
二、语法格式
2.1 HttpServletRequest常用方法
java
// 获取请求参数
String getParameter(String name)
String[] getParameterValues(String name)
Map<String, String[]> getParameterMap()
// 获取请求信息
String getMethod() // GET, POST等
String getRequestURI() // 请求URI
String getQueryString() // 查询字符串
String getRemoteAddr() // 客户端IP
// 获取请求头
String getHeader(String name)
Enumeration<String> getHeaderNames()
// 属性操作
void setAttribute(String name, Object value)
Object getAttribute(String name)
void removeAttribute(String name)
2.2 HttpServletResponse常用方法
java
// 设置状态码
void setStatus(int sc)
void sendError(int sc, String msg)
void sendRedirect(String location)
// 设置响应头
void setHeader(String name, String value)
void addHeader(String name, String value)
void setContentType(String type)
// 输出流
PrintWriter getWriter()
ServletOutputStream getOutputStream()
三、代码案例
3.1 登录重定向示例
java
package com.example.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("<form method='post' action='login'>");
out.println("用户名: <input type='text' name='username'><br>");
out.println("密码: <input type='password' name='password'><br>");
out.println("<input type='submit' value='登录'>");
out.println("</form>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
if ("admin".equals(username) && "123456".equals(password)) {
// 保存用户信息到Session
HttpSession session = req.getSession();
session.setAttribute("user", username);
// 重定向到成功页面
resp.sendRedirect("success");
} else {
// 重定向回登录页
resp.sendRedirect("login");
}
}
}
▶ 运行结果:
LoginServlet 执行效果:
┌─────────────────────────────────────┐
│ GET 请求 (访问 /login): │
│ 浏览器显示登录表单: │
│ ┌─────────────────────────────┐ │
│ │ 用户名: [ ] │ │
│ │ 密码: [ ] │ │
│ │ [登录] │ │
│ └─────────────────────────────┘ │
│ │
│ POST 请求 (admin/123456): │
│ → 重定向到 /success │
│ → 浏览器地址栏变为 /success │
│ → 显示成功页面 │
│ │
│ POST 请求 (错误密码): │
│ → 重定向到 /login │
│ → 浏览器地址栏变为 /login │
│ → 重新显示登录表单 │
└─────────────────────────────────────┘
3.2 转发示例
java
package com.example.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/dispatch")
public class DispatchServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 设置请求属性
req.setAttribute("message", "这是通过转发传递的数据");
req.setAttribute("timestamp", new java.util.Date().toString());
// 转发到另一个Servlet
RequestDispatcher dispatcher = req.getRequestDispatcher("/target");
dispatcher.forward(req, resp);
}
}
@WebServlet("/target")
public class TargetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 获取转发过来的属性
String message = (String) req.getAttribute("message");
String timestamp = (String) req.getAttribute("timestamp");
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println("<h1>目标页面</h1>");
resp.getWriter().println("<p>消息: " + message + "</p>");
resp.getWriter().println("<p>时间: " + timestamp + "</p>");
}
}
▶ 运行结果:
转发示例执行效果:
┌─────────────────────────────────────┐
│ 访问 /dispatch: │
│ → DispatchServlet 设置属性 │
│ → 转发到 /target │
│ → 浏览器地址栏仍为 /dispatch │
│ │
│ 浏览器显示: │
│ ┌─────────────────────────────┐ │
│ │ <h1>目标页面</h1> │ │
│ │ <p>消息: 这是通过转发传递的数据</p>│ │
│ │ <p>时间: 2026-09-02 │ │
│ │ 10:00:00</p> │ │
│ └─────────────────────────────┘ │
│ │
│ 特点: │
│ → URL 不变 (仍为 /dispatch) │
│ → 共享 request 域数据 │
│ → 服务器内部跳转 │
└─────────────────────────────────────┘
3.3 文件下载
java
package com.example.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.*;
import java.net.URLEncoder;
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String filename = req.getParameter("filename");
if (filename == null) {
filename = "test.txt";
}
// 设置响应头
String encodedFilename = URLEncoder.encode(filename, "UTF-8");
resp.setHeader("Content-Disposition",
"attachment;filename=" + encodedFilename);
resp.setContentType("application/octet-stream");
// 获取文件路径
String filePath = getServletContext().getRealPath("/files/" + filename);
File file = new File(filePath);
if (!file.exists()) {
resp.sendError(404, "文件不存在");
return;
}
// 输出文件
try (InputStream in = new FileInputStream(file);
OutputStream out = resp.getOutputStream()) {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
}
}
}
▶ 运行结果:
文件下载执行效果:
┌─────────────────────────────────────┐
│ 访问 /download?filename=test.txt: │
│ │
│ 浏览器行为: │
│ → 弹出下载对话框 │
│ → 提示保存文件 │
│ → 文件名: test.txt │
│ │
│ 响应头: │
│ Content-Disposition: attachment; │
│ filename=test.txt │
│ Content-Type: application/ │
│ octet-stream │
│ │
│ 文件不存在时: │
│ → 返回 404 错误页面 │
│ → 显示 "文件不存在" │
└─────────────────────────────────────┘
3.4 响应头设置
java
package com.example.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
@WebServlet("/headers")
public class HeaderServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 设置响应头
resp.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
resp.setHeader("Pragma", "no-cache");
resp.setDateHeader("Expires", 0);
// 自定义响应头
resp.setHeader("X-Custom-Header", "MyValue");
// 读取请求头
String userAgent = req.getHeader("User-Agent");
String accept = req.getHeader("Accept");
String host = req.getHeader("Host");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("<h1>请求头信息</h1>");
out.println("<p>User-Agent: " + userAgent + "</p>");
out.println("<p>Accept: " + accept + "</p>");
out.println("<p>Host: " + host + "</p>");
}
}
▶ 运行结果:
HeaderServlet 执行效果:
┌─────────────────────────────────────┐
│ 访问 /headers: │
│ │
│ 浏览器显示: │
│ ┌─────────────────────────────┐ │
│ │ <h1>请求头信息</h1> │ │
│ │ <p>User-Agent: Mozilla/5.0 │ │
│ │ (Windows NT 10.0; Win64; │ │
│ │ x64) AppleWebKit/537.36 │ │
│ │ </p> │ │
│ │ <p>Accept: text/html,... │ │
│ │ </p> │ │
│ │ <p>Host: localhost:8080</p> │ │
│ └─────────────────────────────┘ │
│ │
│ 响应头: │
│ Cache-Control: no-cache, no-store, │
│ must-revalidate │
│ Pragma: no-cache │
│ Expires: 0 │
│ X-Custom-Header: MyValue │
└─────────────────────────────────────┘
四、常见错误
| 错误 | 原因 | 解决方案 |
|---|---|---|
| 中文乱码 | 编码不一致 | 请求用setCharacterEncoding,响应用setContentType |
| Cannot forward after response has committed | 响应已提交 | 转发前不要调用getWriter或sendRedirect |
| 重定向丢失数据 | 重定向不共享request域 | 使用Session传递数据 |
| getParameter返回null | 参数不存在 | 使用前先判空 |
| 文件下载乱码 | 文件名编码问题 | 使用URLEncoder编码文件名 |
五、课后练习
- 实现一个登录功能,成功后重定向到欢迎页面,失败后转发回登录页并显示错误信息
- 创建一个Servlet,读取所有请求头并显示在页面上
- 实现文件下载功能,支持中文文件名
- 比较转发和重定向的区别,分别在什么场景下使用
六、本课小结
| 知识点 | 说明 |
|---|---|
| HttpServletRequest | 封装HTTP请求,获取参数、请求头等 |
| HttpServletResponse | 封装HTTP响应,设置状态码、响应头等 |
| 转发(Forward) | 服务器内部跳转,URL不变,共享request |
| 重定向(Redirect) | 客户端跳转,URL改变,不共享request |
| 编码处理 | 请求和响应都需要设置UTF-8 |
本课程持续更新中,欢迎关注!