第30课-请求与响应

第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编码文件名

五、课后练习

  1. 实现一个登录功能,成功后重定向到欢迎页面,失败后转发回登录页并显示错误信息
  2. 创建一个Servlet,读取所有请求头并显示在页面上
  3. 实现文件下载功能,支持中文文件名
  4. 比较转发和重定向的区别,分别在什么场景下使用

六、本课小结

知识点 说明
HttpServletRequest 封装HTTP请求,获取参数、请求头等
HttpServletResponse 封装HTTP响应,设置状态码、响应头等
转发(Forward) 服务器内部跳转,URL不变,共享request
重定向(Redirect) 客户端跳转,URL改变,不共享request
编码处理 请求和响应都需要设置UTF-8

本课程持续更新中,欢迎关注!

相关推荐
CV工程师丁Sir9 小时前
ArkWeb 手记 04|DevTools 调试与缓存清理
java·spring·缓存·harmonyos
Sarvartha15 小时前
final 关键字
java·开发语言
2601_9520477915 小时前
单一策略深度教程:用轻易云把集成任务的报错实时推送到钉钉机器人
java·机器人·钉钉
数据狐(Datafox)16 小时前
淘宝图片搜索 API 落地实战:基于以图搜货搭建跨境电商选品系统
java·大数据·微服务
Escalating_xu18 小时前
【C 语言】数据在内存中的存储:补码、大小端、整型陷阱与 IEEE 754 全解析
java·c语言·网络
灯澜忆梦18 小时前
【面向对象编程C++】| 基础语法
java·c++·算法
一个风轻云淡18 小时前
GCC 和 GDB命令简单解读
java·linux·前端
省钱兄--zs18 小时前
西安24小时自助健身房解决方案实战:系统开发与部署全流程指南
java·spring boot·系统架构·intellij-idea·需求分析