杰瑞科技汇

Java Servlet请求如何处理与交互?

  1. Servlet 请求生命周期:一个请求从进入到离开的完整过程。
  2. 核心 API:HttpServletRequest:这是获取请求信息的核心对象。
  3. 处理请求参数:如何获取表单数据、URL 参数等。
  4. 处理请求头:如何获取客户端发送的头部信息。
  5. 处理请求体:如何获取 POST 请求中的原始数据(如 JSON)。
  6. 请求属性:在请求范围内共享数据。
  7. 一个完整的示例:一个包含 GET 和 POST 请求处理的完整 Servlet。
  8. 现代替代方案:简述 Servlet 的演进。

Servlet 请求生命周期

当一个客户端(如浏览器)向服务器发送请求时,Servlet 容器(如 Tomcat)会执行以下步骤:

  1. 接收请求:容器监听特定端口,接收到针对某个 Servlet 的请求。
  2. 创建 RequestResponse 对象:容器创建一个 HttpServletRequest 对象(代表请求)和一个 HttpServletResponse 对象(代表响应)。
  3. 调用 service() 方法:容器根据请求的 HTTP 方法(GET, POST, PUT, DELETE 等),调用 Servlet 实例的 service() 方法。
  4. 分发请求service() 方法内部会根据 HTTP 方法,将请求分发给对应的 doGet(), doPost(), doPut(), doDelete() 等方法。我们通常只需要重写这些 doXxx() 方法即可
  5. 处理业务逻辑:在 doGet()doPost() 方法中,我们通过 HttpServletRequest 获取请求信息,处理业务逻辑,然后通过 HttpServletResponse 生成响应。
  6. 发送响应:业务逻辑处理完毕后,容器将 HttpServletResponse 中的内容发送回客户端。
  7. 销毁:当 Servlet 不再需要时(例如服务器关闭),容器会调用其 destroy() 方法进行资源清理。

核心 API:HttpServletRequest

HttpServletRequest 接口是 Servlet API 的核心,它封装了所有的 HTTP 请求信息,在 doGet()doPost() 方法中,它作为第一个参数传入。

常用方法按功能分类如下:

功能类别 常用方法 描述
请求行信息 String getMethod() 获取 HTTP 方法 (GET, POST 等)
String getRequestURI() 获取请求的 URI (e.g., /myapp/login)
String getQueryString() 获取 URL 中的查询字符串 (e.g., username=jack&age=20)
String getProtocol() 获取协议版本 (e.g., HTTP/1.1)
String getRemoteAddr() 获取客户端 IP 地址
请求头信息 String getHeader(String name) 获取指定名称的请求头
Enumeration<String> getHeaderNames() 获取所有请求头的名称
int getIntHeader(String name) 获取指定名称的请求头(int 类型)
请求参数 String getParameter(String name) 获取指定名称的参数值(单个)
String[] getParameterValues(String name) 获取指定名称的参数值(数组,用于复选框)
Enumeration<String> getParameterNames() 获取所有参数的名称
Map<String, String[]> getParameterMap() 获取所有参数的键值对映射
请求体/属性 BufferedReader getReader() 获取请求体的字符流(用于读取文本数据如 JSON)
ServletInputStream getInputStream() 获取请求体的字节流(用于读取文件等二进制数据)
void setAttribute(String name, Object o) 设置请求属性
Object getAttribute(String name) 获取请求属性
Enumeration<String> getAttributeNames() 获取所有属性名称
其他 RequestDispatcher getRequestDispatcher(String path) 获取请求转发器
ServletContext getServletContext() 获取 Servlet 上下文

处理请求参数

这是最常见的场景,通常来自 HTML 表单。

HTML 表单示例 (form.html)

<!DOCTYPE html>
<html>
<head>Servlet Form</title>
</head>
<body>
    <!-- GET 请求示例 -->
    <h2>GET 请求</h2>
    <form action="getParameter" method="GET">
        姓名: <input type="text" name="username"><br>
        年龄: <input type="number" name="age"><br>
        <input type="submit" value="GET 提交">
    </form>
    <hr>
    <!-- POST 请求示例 -->
    <h2>POST 请求</h2>
    <form action="getParameter" method="POST">
        爱好:
        <input type="checkbox" name="hobby" value="reading"> 阅读
        <input type="checkbox" name="hobby" value="sports"> 运动
        <input type="checkbox" name="hobby" value="music"> 音乐<br>
        <input type="submit" value="POST 提交">
    </form>
</body>
</html>

Servlet 处理示例 (ParameterServlet.java)

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/getParameter") // 使用注解映射 URL
public class ParameterServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    // 处理 GET 请求
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 设置响应内容类型和字符编码
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        // 2. 获取单个参数
        String username = request.getParameter("username");
        String ageStr = request.getParameter("age");
        out.println("<h1>GET 请求参数</h1>");
        out.println("<p>用户名: " + (username != null ? username : "未提供") + "</p>");
        out.println("<p>年龄: " + (ageStr != null ? ageStr : "未提供") + "</p>");
    }
    // 处理 POST 请求
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 设置响应内容类型和字符编码
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        // 2. 获取数组参数(用于复选框等)
        String[] hobbies = request.getParameterValues("hobby");
        out.println("<h1>POST 请求参数</h1>");
        if (hobbies != null && hobbies.length > 0) {
            out.println("<p>你的爱好是: " + Arrays.toString(hobbies) + "</p>");
        } else {
            out.println("<p>你没有选择任何爱好。</p>");
        }
    }
}

注意

  • getParameter() 返回的是 String 类型,需要自己进行类型转换(如 Integer.parseInt(ageStr))。
  • 对于复选框,多个值会使用相同的 name,因此需要用 getParameterValues() 来获取一个字符串数组。
  • 处理中文乱码:在获取参数之前,如果请求是 POST 且可能包含中文,最好先设置请求编码。request.setCharacterEncoding("UTF-8"); (需要 Tomcat 8+ 版本,否则需要手动处理)。

处理请求头

获取客户端发送的头部信息,User-Agent, Accept-Language 等。

// 在 doGet 或 doPost 方法中
String userAgent = request.getHeader("User-Agent");
String acceptLanguage = request.getHeader("Accept-Language");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<p>浏览器信息: " + userAgent + "</p>");
out.println("<p>接受的语言: " + acceptLanguage + "</p>");

处理请求体(如 JSON 数据)

当使用 AJAX 发送 POST 请求(Content-Type 为 application/json)时,数据不在参数中,而在请求体里,此时需要读取请求体的原始数据。

假设前端发送了一个 JSON 字符串:{"name":"John", "city":"New York"}

Servlet 处理示例

import java.io.BufferedReader;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.gson.Gson; // 需要引入 Google Gson 库
@WebServlet("/json")
public class JsonRequestServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 设置响应类型
        response.setContentType("application/json;charset=UTF-8");
        // 2. 从请求体中读取数据
        StringBuilder sb = new StringBuilder();
        String line;
        try (BufferedReader reader = request.getReader()) {
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        }
        String jsonPayload = sb.toString();
        // 3. 使用 JSON 库解析数据 (这里用 Gson 作为示例)
        Gson gson = new Gson();
        // 假设我们有一个 Person 类
        Person person = gson.fromJson(jsonPayload, Person.class);
        // 4. 处理数据并返回响应
        response.getWriter().write("{\"status\":\"success\", \"message\":\"Received data for " + person.getName() + "\"}");
    }
}
// 简单的 POJO 类
class Person {
    private String name;
    private String city;
    // Getters and Setters (省略)
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getCity() { return city; }
    public void setCity(String city) { this.city = city; }
}

请求属性

请求属性用于在同一个请求的多个组件之间共享数据,最典型的场景是请求转发

// 在 Servlet1 中
request.setAttribute("user", "Alice"); // 设置属性
RequestDispatcher dispatcher = request.getRequestDispatcher("/servlet2"); // 转发到另一个 Servlet
dispatcher.forward(request, response); // 执行转发
// 在 Servlet2 中 (由 Servlet1 转发而来)
String user = (String) request.getAttribute("user"); // 获取属性
// user 的值是 "Alice"

请求转发 (forward) vs 重定向 (redirect)

  • 转发:服务器端行为,URL 不会改变,共享同一个 requestresponse 对象,速度快。
  • 重定向:客户端行为,服务器返回一个 302 状态码和新的 URL,浏览器会重新向新 URL 发送请求,URL 会改变,request 对象是新的。

一个完整的示例

这是一个整合了 GET/POST 参数、请求头和请求属性的完整示例。

HelloServlet.java

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 获取请求参数
        String name = request.getParameter("name");
        if (name == null || name.isEmpty()) {
            name = "Guest"; // 默认值
        }
        // 2. 获取请求头
        String userAgent = request.getHeader("User-Agent");
        // 3. 设置请求属性,用于在 JSP 中使用
        request.setAttribute("welcomeMessage", "Hello, " + name + "!");
        request.setAttribute("browserInfo", userAgent);
        // 4. 转发到 JSP 页面来显示结果
        request.getRequestDispatcher("/welcome.jsp").forward(request, response);
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // POST 请求处理完后会重定向到另一个页面,避免刷新重复提交
        String feedback = request.getParameter("feedback");
        // 简单的业务逻辑处理
        System.out.println("Received feedback: " + feedback);
        // 重定向到感谢页面
        response.sendRedirect("thankyou.html");
    }
}

welcome.jsp (位于 webapp 目录下)

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>Welcome Page</title>
</head>
<body>
    <h1>${welcomeMessage}</h1>
    <p>你的浏览器是: ${browserInfo}</p>
    <p>这是通过请求转发过来的页面。</p>
</body>
</html>

现代替代方案

虽然 Servlet 是基石,但现代 Java Web 开发框架(如 Spring MVC)极大地简化了开发,它们底层依然基于 Servlet,但提供了更高级的抽象。

  • Spring MVC: 使用 @Controller 注解标记控制器类,用 @RequestMapping@GetMapping/@PostMapping 来映射 URL 和方法,方法参数可以直接绑定到 POJO,自动完成类型转换和解析 JSON/XML,无需手动调用 request.getParameter()

Spring MVC 示例(对比上面的 Servlet):

import org.springframework.web.bind.annotation.*;
@RestController // @Controller + @ResponseBody 的组合
@RequestMapping("/api") // 类级别的路径映射
public class MySpringController {
    // 对应 Servlet 的 doGet
    @GetMapping("/hello") // 方法级别的路径映射,只处理 GET 请求
    public String sayHello(@RequestParam(defaultValue = "Guest") String name,
                           @RequestHeader("User-Agent") String userAgent) {
        // Spring 自动将 URL 参数 name 绑定到方法参数 name
        // Spring 自动将请求头 User-Agent 绑定到方法参数 userAgent
        return "Hello, " + name + "! Your browser: " + userAgent;
    }
    // 对应 Servlet 的 doPost
    @PostMapping("/feedback")
    public String processFeedback(@RequestBody Feedback feedback) {
        // Spring 自动将请求体中的 JSON/XML 绑定到 Feedback 对象
        System.out.println("Received feedback: " + feedback.getContent());
        return "Feedback received successfully!";
    }
}
class Feedback {
    private String content;
    // getters and setters...
}

可以看到,Spring MVC 通过约定优于配置和强大的自动绑定机制,让代码更加简洁、易读。

  • Servlet 是 Java Web 的基石,理解其请求处理机制至关重要。
  • HttpServletRequest 是获取所有请求信息的核心接口。
  • 根据数据来源(URL参数、请求头、请求体)选择合适的方法(getParameter, getHeader, getReader)。
  • 请求转发重定向是控制流程的两个重要手段。
  • 现代框架(如 Spring)在 Servlet 之上提供了更优雅的开发体验,但底层原理相通。
分享:
扫描分享到社交APP
上一篇
下一篇