第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

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

相关推荐
源码集结号36 分钟前
云诊所门诊系统源码,基于SpringBoot+Vue.js架构,支持多租户设计与Docker容器化部署
java·spring boot·源码·医院信息系统·门诊系统·诊所系统
论文复现现场1 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
啃瓜子的松鼠1 小时前
泛微OAe9-后端二开异步调用
java
秋天的怀念_David2 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
外收内放2 小时前
Python基础语法练习题(73)
java·前端·python
Wang's Blog2 小时前
Java 项目实战: 交友项目 - 环信后台发送消息测试
java·交友
用户3721574261353 小时前
Java 操作 Word 脚注和尾注:添加、读取与删除
java
小嘉丶学长3 小时前
Day14 · 字典与集合
java·开发语言·网络·python·学习·1024程序员节
字节探索3 小时前
Spring Boot 接口幂等性设计:从唯一索引到 Redis+Lua 的工程实践
java·spring boot
用户3721574261353 小时前
Java 实现 Excel 与 Markdown 相互转换:表格导出、格式设置与多表提取
java