SpringMVC 实战小项目
- 3.1 加法计算器
- 3.1.1 准备⼯作
- 前端
- 3.1.2 约定前后端交互接⼝
- 需求分析
- 接⼝定义
- 请求参数:
- 响应数据:
- 3.1.3 服务器代码
- 3.2 ⽤⼾登录
- 3.2.1 准备⼯作
- 3.2.2 约定前后端交互接⼝
- 3.2.3 实现服务器端代码
- 3.3 留⾔板
- 实现服务器端代码
- 3.4 图书管理系统
- 准备
- 后端
3.1 加法计算器
需求: 输⼊两个整数, 点击"点击相加"按钮, 显⽰计算结果
3.1.1 准备⼯作
创建SpringBoot项⽬: 引⼊Spring Web依赖, 把前端⻚⾯放在项⽬中(课件中提供)
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="calc/sum" method="post">
<h1>计算器</h1>
数字1:<input name="num1" type="text"><br>
数字2:<input name="num2" type="text"><br>
<input type="submit" value=" 点击相加 ">
</form>
</body>
</html>
3.1.2 约定前后端交互接⼝
需求分析
加法计算器功能, 对两个整数进⾏相加, 需要客⼾端提供参与计算的两个数, 服务端返回这两个整数计算的结果
接⼝定义
请求路径:calc/sum
请求⽅式:GET/POST
接⼝描述:计算两个整数相加
请求参数:
⽰例: num1=5&num2=3
响应数据:
Content-Type: text/html
响应内容: 计算机计算结果: 8
3.1.3 服务器代码
@RestController
@RequestMapping("/calc")
public class CalcController {
@RequestMapping("/sum")
public String sum(Integer num1,Integer num2){
Integer sum = num1+num2;
return "<h1>计算机计算结果: "+sum+"</h1>";
}
}
3.2 ⽤⼾登录
需求: ⽤⼾输⼊账号和密码, 后端进⾏校验密码是否正确
- 如果不正确, 前端进⾏⽤⼾告知
- 如果正确, 跳转到⾸⻚. ⾸⻚显⽰当前登录⽤⼾
- 后续再访问⾸⻚, 可以获取到登录⽤⼾信息
3.2.1 准备⼯作
把前端⻚⾯放在项⽬中(课件中提供)
码云地址: 前端代码/⽤⼾登录
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h1>用户登录</h1>
用户名:<input name="userName" type="text" id="userName"><br>
密码:<input name="password" type="password" id="password"><br>
<input type="button" value="登录" onclick="login()">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
function login() {
$.ajax({
type: "post",
url: "/login/check",
data:{
userName: $("#userName").val(),
password: $("#password").val()
},
success: function(result){
if(result==true){
//用户名和密码正确
location.href = "index.html";
// location.assign("index.html");
// location.replace("index.html")
}else{
alert("用户名或密码错误!");
}
}
});
}
</script>
</body>
</html>
3.2.2 约定前后端交互接⼝
需求分析
对于后端开发⼈员⽽⾔, 不涉及前端⻚⾯的展⽰, 只需要提供两个功能
- 登录⻚⾯: 通过账号和密码, 校验输⼊的账号密码是否正确, 并告知前端
- ⾸⻚: 告知前端当前登录⽤⼾. 如果当前已有⽤⼾登录, 返回登录的账号, 如果没有, 返回空
接⼝定义
- 校验接⼝
请求路径:/user/login
请求⽅式:POST
接⼝描述:校验账号密码是否正确
3.2.3 实现服务器端代码
@RequestMapping("/login")
@RestController
public class LoginController {
@RequestMapping("/check")
public boolean check(String userName, String password, HttpSession session){
System.out.println("接收到参数 userName:"+userName + ",password:"+password);
//校验账号和密码是否为空
// if (userName==null || "".equals(userName) || password==null || "".equals(password)){
// return false;
// }
if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){
return false;
}
//校验账号和密码是否正确
//数据模拟
if ("zhangsan".equals(userName) && "123456".equals(password)){
session.setAttribute("userName",userName);
return true;
}
return false;
}
@RequestMapping("/index")
public String index(HttpSession session){
String userName = (String)session.getAttribute("userName");
return userName;
}
}
3.3 留⾔板
需求:
界⾯如下图所⽰
- 输⼊留⾔信息, 点击提交. 后端把数据存储起来.
- ⻚⾯展⽰输⼊的表⽩墙的信息
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>留言板</title>
<style>
.container {
width: 350px;
height: 300px;
margin: 0 auto;
/* border: 1px black solid; */
text-align: center;
}
.grey {
color: grey;
}
.container .row {
width: 350px;
height: 40px;
display: flex;
justify-content: space-between;
align-items: center;
}
.container .row input {
width: 260px;
height: 30px;
}
#submit {
width: 350px;
height: 40px;
background-color: orange;
color: white;
border: none;
margin: 10px;
border-radius: 5px;
font-size: 20px;
}
</style>
</head>
<body>
<div class="container">
<h1>留言板</h1>
<p class="grey">输入后点击提交, 会将信息显示下方空白处</p>
<div class="row">
<span>谁:</span> <input type="text" name="" id="from">
</div>
<div class="row">
<span>对谁:</span> <input type="text" name="" id="to">
</div>
<div class="row">
<span>说什么:</span> <input type="text" name="" id="say">
</div>
<input type="button" value="提交" id="submit" onclick="submit()">
<!-- <div>A 对 B 说: hello</div> -->
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
//页面加载时, 显示留言信息
//从后端获取到留言信息, 并显示在页面上
$.ajax({
type: "get",
url: "/message/getList",
success: function(messages){
for(var message of messages){
var html = "<div>"+message.from+" 对 "+message.to+" 说: "+message.message+"</div>";
$(".container").append(html);
}
}
});
function submit() {
//1. 获取留言的内容
var from = $('#from').val();
var to = $('#to').val();
var say = $('#say').val();
if (from == '' || to == '' || say == '') {
return;
}
$.ajax({
type: "post",
url: "/message/publish",
data: {
from: from,
to: to,
message: say
},
success: function (result) {
if (result == true) {
//添加成功
//2. 构造节点
var divE = "<div>" + from + "对" + to + "说:" + say + "</div>";
//3. 把节点添加到页面上
$(".container").append(divE);
//4. 清空输入框的值
$('#from').val("");
$('#to').val("");
$('#say').val("");
}else{
alert("发表失败");
}
}
});
}
</script>
</body>
</html>
实现服务器端代码
@Data
public class MessageInfo {
private Integer id;
private String from;
private String to;
private String message;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}
@RequestMapping("/message")
@RestController
public class MessageController {
@Autowired
private MessageService messageService;
private List<MessageInfo> messageInfos = new ArrayList<>();
@RequestMapping("/publish")
public boolean publishMessage(MessageInfo messageInfo){
if (!StringUtils.hasLength(messageInfo.getFrom())
|| !StringUtils.hasLength(messageInfo.getTo())
|| !StringUtils.hasLength(messageInfo.getMessage())){
return false;
}
//暂时存放在内存中
// messageInfos.add(messageInfo);
//把数据放在mysql当中
messageService.insertMessage(messageInfo);
return true;
}
@RequestMapping("/getList")
public List<MessageInfo> getList(){
return messageService.queryList();
}
}
3.4 图书管理系统
需求:
- 登录: ⽤⼾输⼊账号,密码完成登录功能
- 列表展⽰: 展⽰图书
准备
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/login.css">
<script type="text/javascript" src="js/jquery.min.js"></script>
</head>
<body>
<div class="container-login">
<div class="container-pic">
<img src="pic/computer.png" width="350px">
</div>
<div class="login-dialog">
<h3>登陆</h3>
<div class="row">
<span>用户名</span>
<input type="text" name="userName" id="userName" class="form-control">
</div>
<div class="row">
<span>密码</span>
<input type="password" name="password" id="password" class="form-control">
</div>
<div class="row">
<button type="button" class="btn btn-info btn-lg" onclick="login()">登录</button>
</div>
</div>
</div>
<script src="js/jquery.min.js"></script>
<script>
function login() {
$.ajax({
type:"post",
url: "/user/login",
data:{
userName: $("#userName").val(),
password: $("#password").val()
},
success:function(result){
console.log(result);
if(result.code==200 && result.data==true){
//验证成功
location.href = "book_list.html";
}else{
alert("用户名或密码失败");
}
}
});
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图书列表展示</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/list.css">
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script src="js/jq-paginator.js"></script>
</head>
<body>
<div class="bookContainer">
<h2>图书列表展示</h2>
<div class="navbar-justify-between">
<div>
<button class="btn btn-outline-info" type="button" onclick="location.href='book_add.html'">添加图书</button>
<button class="btn btn-outline-info" type="button" onclick="batchDelete()">批量删除</button>
</div>
</div>
<table>
<thead>
<tr>
<td>选择</td>
<td class="width100">图书ID</td>
<td>书名</td>
<td>作者</td>
<td>数量</td>
<td>定价</td>
<td>出版社</td>
<td>状态</td>
<td class="width200">操作</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="demo">
<ul id="pageContainer" class="pagination justify-content-center"></ul>
</div>
<script>
getBookList();
function getBookList() {
$.ajax({
type: "get",
url: "/book/getListByPage" + location.search,
success: function (result) {
console.log("后端返回成功");
if (result.code == -1) {
alert("发生内部错误, 请联系管理员");
return;
}
var data = result.data;
var books = data.records;
console.log(books);
var finalHtml = "";
for (var book of books) {
//拼接html
finalHtml += '<tr>';
finalHtml += '<td><input type="checkbox" name="selectBook" value="' + book.id + '" id="selectBook" class="book-select"></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>' + book.stateCN + '</td>';
finalHtml += '<td>';
finalHtml += '<div class="op">';
finalHtml += '<a href="book_update.html?bookId=' + book.id + '">修改</a>';
finalHtml += '<a href="javascript:void(0)" οnclick="deleteBook(' + book.id + ')">删除</a>';
finalHtml += '</div>';
finalHtml += '</td>';
finalHtml += '</tr>';
}
$("tbody").html(finalHtml);
//处理翻页信息
//翻页信息
$("#pageContainer").jqPaginator({
totalCounts: data.count, //总记录数
pageSize: 10, //每页的个数
visiblePages: 5, //可视页数
currentPage: data.pageRequest.currentPage, //当前页码
first: '<li class="page-item"><a class="page-link">首页</a></li>',
prev: '<li class="page-item"><a class="page-link" href="javascript:void(0);">上一页<\/a><\/li>',
next: '<li class="page-item"><a class="page-link" href="javascript:void(0);">下一页<\/a><\/li>',
last: '<li class="page-item"><a class="page-link" href="javascript:void(0);">最后一页<\/a><\/li>',
page: '<li class="page-item"><a class="page-link" href="javascript:void(0);">{{page}}<\/a><\/li>',
//页面初始化和页码点击时都会执行
onPageChange: function (page, type) {
console.log("第" + page + "页, 类型:" + type);
if (type == "change") {
// console.log("book_list.html"+location.search);
location.href = "book_list.html?currentPage=" + page;
}
}
});
},
error: function (error) {
console.log(error);
if (error != null && error.status == 401) {
location.href = "login.html";
}
}
});
}
function deleteBook(id) {
var isDelete = confirm("确认删除?");//弹出确认框
if (isDelete) {
console.log(id);
//删除图书
$.ajax({
type: "post",
url: "/book/updateBook",
data: {
id: id,
status: 0
},
success: function (result) {
if (result.code == 200 && result.data == true) {
alert("删除成功");
location.href = "book_list.html";
}
}
});
}
}
function batchDelete() {
var isDelete = confirm("确认批量删除?");
if (isDelete) {
//获取复选框的id
var ids = [];
$("input:checkbox[name='selectBook']:checked").each(function () {
ids.push($(this).val());
});
console.log(ids);
$.ajax({
type: "post",
url: "/book/batchDelete?ids=" + ids,
success: function (result) {
if (result.code==200 && result.data == true) {
//删除成功
location.href = "book_list.html";
} else {
alter("删除失败, 请联系管理员");
}
}
});
}
}
</script>
</div>
</body>
</html>
根据需求可以得知, 后端需要提供两个接⼝
- 账号密码校验接⼝: 根据输⼊⽤⼾名和密码校验登录是否通过
- 图书列表: 提供图书列表信息
后端
@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;
}
//模拟验证数据, 账号密码正确
if("admin".equals(name) && "admin".equals(password)){
session.setAttribute("userName",name);
return true;
}
//账号密码错误
return false;
}
}
@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 获取图书信息
*
* @return
*/
public List<BookInfo> mockData() {
List<BookInfo> books = new ArrayList<>();
for (int i = 0; i < 5; i++) {
BookInfo book = new BookInfo();
book.setId(i);
book.setBookName("书籍" + i);
book.setAuthor("作者" + i);
book.setCount(i * 5 + 3);
book.setPrice(new BigDecimal(new Random().nextInt(100)));
book.setPublish("出版社" + i);
book.setStatus(1);
books.add(book);
}
return books;
}
}