图书管理系统
1. 准备前端代码
前端资源可参考项目仓库获取,这里主要关注后端接口设计与前后端联调逻辑。
2. 约定前后端交互接口
这是一个相对完整的项目案例,我们先聚焦核心功能实现:用户登录与图书列表展示。

2.1 登录接口

2.2 图书列表展示

字段说明:
| 字段 | 说明 |
|---|---|
| id | 图书 ID |
| bookName | 图书名称 |
| author | 作者 |
| count | 数量 |
| price | 定价 |
| publish | 图书出版社 |
| status | 图书状态(1 - 可借阅,其他 - 不可借阅) |
| statusCN | 图书状态中文含义 |
3. 后端核心逻辑实现
3.1 实体类定义
创建 BookInfo 类,对应数据库中的图书信息表结构。
@Data
public class BookInfo {
// 图书 ID
private Integer id;
// 书名
private String bookName;
// 作者
private String author;
// 数量
private Integer count;
// 定价
private BigDecimal price;
// 出版社
private String publish;
// 状态 0-不允许借阅 1-允许借阅
private Integer status;
private String statusCN;
// 创建时间
private Date createTime;
// 更新时间
private Date updateTime;
}
3.2 控制器开发
UserController:处理登录验证。
import jakarta.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/user")
@RestController
public class UserController {
@RequestMapping("login")
public boolean login(String name, String password, HttpSession session) {
// 账号密码验证
if (!StringUtils.hasLength(name) || !StringUtils.hasLength(password)) {
return false;
}
// 账号密码正确,账号 admin & 密码 admin
if ("admin".equals(name) && "admin".equals(password)) {
session.setAttribute("userName", name);
return true;
}
// 账号密码错误
return false;
}
}
BookController:获取图书列表。
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@RequestMapping("/book")
@RestController
public class BookController {
@RequestMapping("/getList")
public List<BookInfo> getList() {
// 模拟数据
List<BookInfo> books = mockData();
// 处理页面展示
for (BookInfo book : books) {
if (book.getStatus() == 1) {
book.setStatusCN("可借阅");
} else {
book.setStatusCN("不可借阅");
}
}
return books;
}
/**
* 数据 Mock 获取图书信息
*/
public List<BookInfo> mockData() {
List<BookInfo> books = new ArrayList<>();
for (int i = 1; i <= 15; i++) {
BookInfo book = new BookInfo();
book.setId(i);
book.setBookName("图书" + i);
book.setAuthor("作者" + i);
book.setCount(new Random().nextInt(100));
book.setPrice(new BigDecimal(new Random().nextInt(100)));
book.setPublish("出版社" + i);
book.setStatus(i % 5 == 0 ? 2 : 1);
books.add(book);
}
return books;
}
}
关于 Mock 数据的说明:
在开发和测试过程中,环境可能不稳定或协同同事未完成接口,此时构造真实数据成本较高。Mock(模拟)就是创建虚拟对象或数据样本,辅助开发或测试。简单来说,就是用假数据占位。
将 Mock 逻辑封装成独立方法的好处很明显:代码整洁规范,后续接入真实数据库时,只需替换该方法内部逻辑即可,无需修改调用处。
4. 前端交互与渲染
4.1 登录页面
添加登录处理逻辑,使用 jQuery 发送 AJAX 请求。
<script src="js/jquery.min.js"></script>
<script>
function login() {
$.ajax({
type: "post",
url: "/user/login",
data: {
name: $("#userName").val(),
password: $("#password").val()
},
success: function (result) {
if (result) {
location.href = "book_list.html";
} else {
alert("账号或密码不正确!");
}
}
});
}
</script>
4.2 图书列表展示
删除前端伪造的静态内容,改为从后端获取数据并动态渲染。
- 清理
<body></body>标签中的硬编码内容。 - 完善获取图书方法。
function getBookList() {
var finalHtml = "";
$.ajax({
type: "get",
url: "/book/getList",
success: function (result) {
console.log(result);
if (result != null) {
for (var book of result) {
finalHtml += "<tr><input type='checkbox' name='selectBook' value='" + book.id + "'></td>";
finalHtml += "<td>" + book.id + "</td>";
finalHtml += "<td>" + book.bookName + "</td>";
finalHtml += "<td>" + book.author + "</td>";
finalHtml += "<td>" + book.count + "</td>";
finalHtml += "<td>" + book.price + "</td>";
finalHtml += "<td>" + book.publish + "</td>";
finalHtml += "<td><div>" + book.statusCN + "</div></td>";
finalHtml += "<td><a href='book_update.html?bookId=" + book.id + "'>修改</a>";
finalHtml += "<a href='javascript:void(0)' onclick='deleteBook(" + book.id + ")'> 删除</a></td>";
finalHtml += "</tr>";
}
$("tbody").html(finalHtml);
}
}
});
}
5. 问题排查与调试
当效果未达预期时,不要盲目猜测,建议按以下步骤排查:
- 检查两端代码:肉眼可见范围有限,优先通过 Debug 和日志分析。
- 后端测试:使用 Postman 直接调用接口。
- 第一步确认请求是否到达后端(打印参数变量)。
- 第二步检查参数解析和返回结果是否正确。
- 没问题再交给前端联调。
- 前端测试:查看浏览器控制台 Console 日志,确认 Ajax 回调数据是否符合预期。
6. GET 与 POST 的实战区别
在实际开发中,GET 和 POST 的区别主要体现在参数位置和数据格式约定上,而非简单的'能不能传参'。
6.1 参数位置差异
- GET:参数只能放在 URL 中(Postman 里的 Params)。前后端配合简单,不存在格式解析问题。
- POST:参数既可以放在 URL,也可以放在 Body 中。实际开发中最常用的是 Body。
6.2 数据格式一致性
一旦使用 POST 的 Body 传参,前后端必须严格约定格式:
- 前端用 JSON,后端必须按 JSON 解析。
- 前端用 form-data,后端也要用对应方式接收。
- 前端用 x-www-form-urlencoded,后端同样需匹配。
否则会出现'前端传了,后端收不到'的经典问题。
6.3 语义与安全性
- GET:用于获取资源,幂等且可缓存。受 URL 长度限制,不适合传大量数据,且参数暴露在 URL 中,不适合敏感信息。
- POST:用于提交资源,非幂等。无明确大小限制,参数在 Body 中,相对更安全(需配合 HTTPS)。
6.4 Postman 的约束本质
Postman 默认隐藏 GET 请求的 Body 输入框,并非强制禁止,而是遵循行业最佳实践,引导开发者写出符合规范的代码。虽然可以通过抓包工具强行给 GET 加 Body,但这属于非常规操作,存在兼容性风险(如 Nginx、CDN 可能丢弃 Body 数据)和缓存失效问题,不建议在生产环境中使用。
总结:实际开发中,GET 和 POST 的最大区别在于 POST 的 Body 传参需要前后端格式一致,而 GET 没有这个问题。


