第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

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

相关推荐
qq_269506751 小时前
第28课-AJAX与前端框架
java
程序员清风1 小时前
Java 后端如何接入大语言模型
java·spring boot·架构·aigc
xieliyu.1 小时前
JVM 垃圾回收机制详解:从标记过程、回收算法到垃圾收集器
java·jvm·笔记·java-ee
JavacKaka1 小时前
Redis大Key导致生产事故复盘博客-2026-09-18
java
右耳朵猫AI2 小时前
Java周刊2026W38 | Micronaut 修补三漏洞、JDK 27 提速 54%、Jetty 修复抖动测试
java·后端·spring
事已至此先睡覺吧2 小时前
第二篇:Java 基础语法:变量、数据类型、运算符与类型转换
java
Wang's Blog2 小时前
Java 项目实战: 外卖平台-后台退出功能与首页iframe架构
java·服务器·redis
Wang's Blog2 小时前
Java 项目实战: 外卖平台-软件开发流程与项目整体介绍
java·服务器·redis
她说..2 小时前
常见设计模式-模板方法模式
java·spring·设计模式·springboot