web-第六次课后作业

本次作业是进一步了解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,张三!
    }
}

如果是对象(如 ResultEmpList),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;
    }
}

为什么 dataObject 类型?

因为 data 可以是任何数据------List<Emp>EmpStringMap 等。用 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 类包装所有返回值

相关推荐
何以解忧,唯有..1 小时前
Go语言类型转换详解:从基础到进阶实践
开发语言·后端·golang
爱勇宝1 小时前
CEO通知5100名员工:今年不涨薪了,钱要投给AI!
前端·后端·程序员
何以解忧,唯有..1 小时前
Go 语言指针类型详解:从基础到实战
开发语言·后端·golang
柏舟飞流1 小时前
Spring Boot + Spring Security + RBAC:从登录鉴权到权限模型设计
java·spring boot·spring
乘风gg1 小时前
前端死到第几轮了?得物前端部门解散有感!
前端·ai编程·claude
艾伦野鸽ggg1 小时前
web 组大一下第二次考核
前端·css·html
掘金者阿豪1 小时前
这本讲故事的数学科普书里,藏着AI背后的底层密码
后端
水煮白菜王1 小时前
高德地图"未获得商用授权"水印临时移除方案
前端·javascript
库拉AI小李1 小时前
# 数据清洗与分析:Gemini 3.5 处理 Excel 数据的实操体验
前端·人工智能·后端