本次作业是进一步了解Spring Boot 请求与响应,用到了element plus里面的表格组件。
(另外,用的还是Apifox,不是Postman)
Spring Boot 请求与响应详解
一、两种控制器模式
Spring Boot 提供两种控制器注解,它们的响应方式完全不同:
@RestController |
@Controller |
|
|---|---|---|
| 返回什么 | 数据(字符串、JSON)直接写入响应体 | 页面模板名称,去找对应的 HTML 文件 |
| 典型用途 | REST API 接口、前后端分离 | 传统服务端渲染页面(如 Thymeleaf、JSP) |
| 是否用 Model | 不需要,数据直接返回 | 需要,通过 Model 给页面传数据 |
| 底层原理 | @Controller + @ResponseBody 的组合 |
纯 @Controller,返回值被视图解析器处理 |
示例对比:
java
// RestController:返回的是 JSON 数据
@RestController
public class EmpController {
@GetMapping("/emp/list")
public Result list() {
return Result.success(empList); // 浏览器收到 JSON 字符串
}
}
// Controller:返回的是页面
@Controller
public class RegController {
@GetMapping("/register")
public String registerPage() {
return "register"; // Spring Boot 去找 templates/register.html
}
}
二、请求(Request)详解
3.1 请求路径 ------ 谁来处理这个请求
Spring Boot 通过注解把 URL 路径和 Java 方法绑定在一起。内部维护一张"路径映射表":
GET /hello → HelloController.hello()
GET /register → RegController.registerPage()
POST /register → RegController.register()
GET /emp/list → EmpController.list()
一个请求进来后,DispatcherServlet(前端控制器,Spring MVC 的核心)查这张表,找到匹配的路径就调用对应方法。
常用注解:
| 注解 | 处理的 HTTP 方法 | 使用场景 |
|---|---|---|
@GetMapping("/xxx") |
GET | 获取数据、访问页面 |
@PostMapping("/xxx") |
POST | 提交表单、新增数据 |
@PutMapping("/xxx") |
PUT | 更新数据 |
@DeleteMapping("/xxx") |
DELETE | 删除数据 |
3.2 请求参数 ------ 客户端怎么把数据带给服务器
方式一:方法参数名自动匹配
java
@GetMapping("/hello")
public String hello(String name) {
return "Hello," + name + "!";
}
// 请求 URL: http://localhost:8080/hello?name=张三
// Spring Boot 发现请求中有 name=张三, 方法参数也叫 name
// 自动赋值: name = "张三"
这种方式简单但不明确,如果请求参数和方法参数名字不一样就匹配不上。
方式二:@RequestParam 明确绑定
java
@PostMapping("/register")
public String register(
@RequestParam String username, // 绑定请求中的 username 参数
@RequestParam String password, // 绑定请求中的 password 参数
@RequestParam String phone // 绑定请求中的 phone 参数
) { ... }
// 浏览器发送的 POST 请求体:
// username=张三&password=123456&phone=13800138000
@RequestParam 还可以设置更多属性:
java
@RequestParam(value = "username", required = true) // 默认就是 required=true,必传
@RequestParam(value = "page", defaultValue = "1") // 没传则默认为 "1"
方式三:无参数请求
java
@GetMapping("/emp/list")
public Result list() { // 没有参数,直接访问路径即可触发
...
}
// 请求 URL: http://localhost:8080/emp/list (不需要带 ?xxx=yyy)
三、响应(Response)详解
4.1 @RestController 的响应流程
@RestController 的核心行为是:方法的返回值直接作为 HTTP 响应体返回。
如果是字符串,直接返回字符串:
java
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(String name) {
return "Hello," + name + "!"; // 浏览器收到纯文本: Hello,张三!
}
}
如果是对象(如 Result、Emp、List),Spring Boot 内置的 Jackson 库会把它自动转换为 JSON 字符串:
Java 对象 ──Jackson 序列化──▶ JSON 字符串 ──写入──▶ HTTP 响应体 ──发送──▶ 浏览器
4.2 @Controller 的响应流程
@Controller 的返回值会被视图解析器(ViewResolver) 处理:
java
@Controller
public class RegController {
@PostMapping("/register")
public String register(@RequestParam String username, Model model) {
model.addAttribute("username", username); // 把数据放进 Model
return "register-success"; // 去找 templates/register-success.html
}
}
流程:
"register-success" ──视图解析器──▶ templates/register-success.html ──渲染──▶ HTML 页面
▲
Model 的数据被填入页面
${username} → "张三"
Model 是一个数据容器 (本质是一个 Map)。你在 Controller 里 model.addAttribute("key", value),在 HTML 模板里就可以用 ${key} 取出来显示。
四、完整实例:Emp 接口全链路拆解(案例简化版)
涉及的三个文件
src/main/java/com/example/demo/
├── pojo/
│ ├── Emp.java ← 实体类,对应 emp.xml 中的每条数据
│ └── Result.java ← 统一响应格式,包装所有返回值
├── utils/
│ └── XmlParserUtils.java ← 工具类,解析 XML 文件
└── controller/
└── EmpController.java ← 控制器,处理 /emp/list 请求
5.1 Emp 实体类
java
package com.example.demo.pojo;
public class Emp {
private String name; // 姓名
private Integer age; // 年龄
private String image; // 头像 URL
private Integer gender; // 性别(1=男, 2=女)
private Integer job; // 职位
// getter / setter ...
// toString ...
}
为什么用 Integer 而不是 int?
Integer 可以为 null,int 不行。如果 XML 中某个字段缺失,Integer 会置为 null,而 int 会抛异常。
5.2 数据源:emp.xml
xml
<emps>
<emp>
<name>张三</name>
<age>25</age>
<image>https://example.com/zhangsan.jpg</image>
<gender>1</gender>
<job>2</job>
</emp>
<emp>
<name>李四</name>
<age>30</age>
<image>https://example.com/lisi.jpg</image>
<gender>2</gender>
<job>3</job>
</emp>
</emps>
5.3 XmlParserUtils 解析工具
java
public class XmlParserUtils {
public static List<Emp> parse() {
List<Emp> empList = new ArrayList<>(); // ① 准备空列表
// ② 从 classpath 找到 emp.xml 文件
InputStream inputStream = XmlParserUtils.class
.getClassLoader().getResourceAsStream("emp.xml");
// ③ 用 Java 内置的 DOM 解析器读取 XML
DocumentBuilder builder = DocumentBuilderFactory
.newInstance().newDocumentBuilder();
Document document = builder.parse(inputStream);
// ④ 获取所有 <emp> 标签
NodeList nodeList = document.getElementsByTagName("emp");
// ⑤ 遍历每个 <emp>,创建 Emp 对象
for (int i = 0; i < nodeList.getLength(); i++) {
Element element = (Element) nodeList.item(i);
Emp emp = new Emp();
emp.setName(getText(element, "name")); // 取 <name> 的值
emp.setAge(Integer.parseInt(getText(element, "age")));
emp.setImage(getText(element, "image"));
emp.setGender(Integer.parseInt(getText(element, "gender")));
emp.setJob(Integer.parseInt(getText(element, "job")));
empList.add(emp); // ⑥ 添加到列表
}
return empList; // ⑦ 返回
}
private static String getText(Element element, String tagName) {
return element.getElementsByTagName(tagName).item(0).getTextContent();
}
}
5.4 Result 统一响应类
java
package com.example.demo.pojo;
public class Result {
private Integer code; // 状态码:1=成功,0=失败
private String msg; // 提示信息
private Object data; // 数据载荷(可以是任何类型)
public static Result success(Object data) {
Result result = new Result();
result.code = 1;
result.msg = "success";
result.data = data;
return result;
}
public static Result error(String msg) {
Result result = new Result();
result.code = 0;
result.msg = msg;
return result;
}
}
为什么
data用Object类型?因为
data可以是任何数据------List<Emp>、Emp、String、Map等。用Object接收一切,实际序列化时 Jackson 会根据真实类型输出对应的 JSON。
5.5 EmpController 控制器
java
@RestController // ← 返回数据,不返回页面
public class EmpController {
@GetMapping("/emp/list") // ← 绑定 GET /emp/list 路径
public Result list() { // ← 无参数,不需要客户端传参
List<Emp> empList = XmlParserUtils.parse(); // ① 从 XML 读数据
return Result.success(empList); // ② 包装后返回
}
}
5.6 完整请求流程图
用户在浏览器输入: http://localhost:8080/emp/list
│
▼
┌──────────────────────────────────────────┐
│ 1. HTTP GET 请求到达 │
│ Method: GET │
│ URL: /emp/list │
│ (无请求参数) │
└──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ 2. Spring Boot 内置 Tomcat 接收请求 │
│ 交给 DispatcherServlet │
└──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ 3. DispatcherServlet 查"路径映射表" │
│ /emp/list 匹配到 │
│ EmpController.list() │
└──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ 4. 执行业务逻辑 │
│ XmlParserUtils.parse() │
│ ├─ 读取 classpath:emp.xml │
│ ├─ DOM 解析 XML │
│ ├─ 为每个 <emp> 创建 Emp 对象 │
│ └─ 返回 List<Emp> │
└──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ 5. 包装结果 │
│ Result.success(empList) │
│ ├─ code = 1 │
│ ├─ msg = "success" │
│ └─ data = [Emp@张三, Emp@李四] │
└──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ 6. @RestController 生效 │
│ Spring Boot 调用 Jackson 库 │
│ 把 Result 对象序列化为 JSON 字符串 │
└──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ 7. HTTP 响应返回 │
│ HTTP/1.1 200 OK │
│ Content-Type: application/json │
│ │
│ { │
│ "code": 1, │
│ "msg": "success", │
│ "data": [ │
│ { │
│ "name": "张三", │
│ "age": 25, │
│ "image": "https://...", │
│ "gender": 1, │
│ "job": 2 │
│ }, │
│ { │
│ "name": "李四", │
│ "age": 30, │
│ "image": "https://...", │
│ "gender": 2, │
│ "job": 3 │
│ } │
│ ] │
│ } │
└──────────────────────────────────────────┘
│
▼
浏览器收到 JSON,前端代码解析并渲染到页面
五、关键细节补充
6.1 为什么 Emp 类需要 getter/setter?
Jackson 序列化 Emp 对象时,是通过调用 getName()、getAge() 等 getter 方法拿值的。如果没有 getter,Jackson 就不知道这个对象有哪些属性,输出的 JSON 就会是空的 {}。
java
// Jackson 的工作原理(简化版):
// 看到 Emp 有 getName() 方法 → 推断它有 "name" 属性 → 调用 getName() 拿值 → 写入 JSON
{
"name": "张三", // 来自 emp.getName()
"age": 25, // 来自 emp.getAge()
...
}
6.2 统一响应格式的好处
每个接口返回的结构完全一致:
json
// 成功时的响应
{ "code": 1, "msg": "success", "data": { ... } }
// 失败时的响应
{ "code": 0, "msg": "用户名已存在", "data": null }
前端只需要写一套判断逻辑:
javascript
if (response.code === 1) {
// 成功,使用 response.data 渲染页面
} else {
// 失败,弹出 response.msg 提示用户
}
6.3 如果误用 @Controller 会怎样?
java
@Controller // 改成这个
public class EmpController {
@GetMapping("/emp/list")
public Result list() {
return Result.success(empList); // 返回值被当成页面名 "Result"
} // Spring Boot 去找 templates/Result.html
} // 找不到 → 报错
六、记忆速查
| 要做什么 | 用什么 |
|---|---|
| 返回 JSON 数据 | @RestController |
| 返回 HTML 页面 | @Controller |
| 绑定 GET 请求 | @GetMapping("/path") |
| 绑定 POST 请求 | @PostMapping("/path") |
| 接收 URL 参数 | @RequestParam 或方法参数名自动匹配 |
| 给页面传数据 | Model.addAttribute("key", value) |
| 对象转 JSON | Jackson 自动完成(@RestController 自带) |
| 统一返回格式 | 自定义 Result 类包装所有返回值 |


