Spring MVC练习(前后端分离开发实例)

ops/2024/11/30 18:36:40/
White graces:个人主页

🙉专栏推荐:Java入门知识🙉

🐹今日诗词:二十五弦弹夜月,不胜清怨却飞来🐹


⛳️点赞 ☀️收藏⭐️关注💬卑微小博主🙏

⛳️点赞 ☀️收藏⭐️关注💬卑微小博主🙏


目录

加法计算器

前端页面

约定前后端交互接口

用户登录

约定前后端交互接口

查询用户登录接口

前端页面

同步操作和异步操作

留言板

约定前后端接口

public接口

getList接口

图书管理系统

 应用分层(重点)

美图分享


加法计算器

前端页面

这里的计算器是后端交互的方式, 单纯的前端也可以实现, 但是就达不到练习的目的了

准备工作: 我们先搭建一个简单的页面, 代码和效果如下: 

<!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>

约定前后端交互接口

  • 简单来说,就是允许客户端给服务器发送哪些HTTP请求,并且每种请求预期获取什么样的HTTP响应, 不能请求A, 你给我返回B, 请求响应必须正确的
  • 现在"前后端分离"模式开发,前端后端代码通常由不同的团队负责开发.双⽅团队在开发之前,会提前 约定好交互的方式
  • 把约定的内容写在⽂档上,就是"接⼝⽂档",接 ⼝⽂档也可以理解为是应⽤程序的"操作说明书

因此后端代码这样写

java">import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/calc")
public class CalcController {@RequestMapping("/sum")public String sum(Integer num1, Integer num2) {Integer sum = num1 + num2;return "计算结果: " + sum;}
}

效果演示

用户登录

约定前后端交互接口

login方法(登录)

首先就是参数效验, 用户名和密码肯定不能为null或者空

参数效验: 1. 可以用if进行多次效验判断(麻烦一点)

2. 使用StringUtils类的hasLength方法进行效验(推荐)

这是登录效验代码

java">    @RequestMapping("/login")public Boolean login(String username, String password, HttpSession session) {//参数效验(null和空)if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return false;}//效验账号密码是否正确//这里使用硬编码方式演示, 数据库操作效验密码不方便//假设账号密码是: "zhangsan" "123456"if ("zhangsan".equals(username) && "123456".equals(password)) {//设置Session, 需要创建Sessionsession.setAttribute("username", username);return true;}return false;}

测试一下接口有没问题

查询用户登录接口

java">​
@RequestMapping("/getUserInfo")public String getUserInfo(HttpSession session) {String username = (String) session.getAttribute("username");return username==null?"":username;}​
这样后端接口就写好了, 接下来搞定前端

前端页面

需要用到Ajax

Ajax,全称是Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)

作用: 

  • 异步:用户在等待服务器响应时可以继续操作界面,提高了用户体验。
  • 无需刷新:局部更新页面,不需要重新加载整个页面。
  • 数据交换格式:虽然名字中包含 XML,但 Ajax 可以使用多种数据格式,包括 JSON、HTML 等,不仅限于 XML

同步操作和异步操作

举例解释二者区别

比如去买饭, 我要卖蛋炒饭和奶茶, 正常情况下是买完蛋炒饭, 等老板做完然后去买奶茶

这种情况就是同步操作

异步操作就是: 我先预购蛋炒饭, 然后我去买奶茶, 等蛋炒饭做好了老板通知去拿就可以了

登录界面

通过Ajax, 登录成功进行页面跳转(重定向), 登录失败就提示错误

<!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="jquery-3.7.0.min.js"></script><script>function login() {$.ajax({url: "/user/login",  //指定请求的URL地址type: "post",    //指定请求的类型data: {  //指定要发送的数据, 这里的数据是从表单中获取的用户名和密码"userName": $("#userName").val(), // 获取用户名输入框的值"password": $("#password").val() // 获取密码输入框的值},//指定请求成功时的回调函数success: function (result) {// 如果服务器返回的结果为 true,进行页面跳转if (result) {location.href = "index.html"; //重定向到index.html页面} else {alert("密码错误"); //如果结果为 false,弹出密码错误的提示}}});}</script>
</body></html>
<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>用户登录首页</title>
</head><body>登录人: <span id="loginUser"></span><script src="jquery-3.7.0.min.js"></script><script>$.ajax({url: "user/getUserInfo",type: "get",success: function (userName) {$("#loginUser").text(userName);}});</script>
</body></html>

留言板

约定前后端接口

设计思路: 

点击提交, 要把数据拼接到留言板下方,

同时需要把数据提交到后端, 交给后端存储, 我们可以通过一个链表存储

并且第一使用留言板, 还要把以前的留言显示在下面(从后端拿到数据并显示在页面上)

因此我们要设计两个接口

  1. message/public接口: 收集前端提交的数据
  2. message/getList接口: 返回数据到前端

因此我们可以这样约定前后端接口

public接口

java">    @RequestMapping(value = "/public", method = RequestMethod.POST)public Boolean publish(@RequestBody MessageInfo messageInfo) {list.add(messageInfo);System.out.println("日志: 后端存储数据成功");return true;}

getList接口

我们来测试一下接口是否正确

这样后端的代码就没问题了, 接下来我们来写, 前端页面如何通过接口来获取后端数据的代码

前端获取后端数据的代码

分成两步: 

  1.  首先将以前的留言记录显示到下面, 不能每次打开都要重新留言

    由于html代码从上向下执行, 所以这串逻辑代码必须写在script的开头

  2. 创建新的留言, 将新的留言数据交给后端保存, 同时将留言显示到下方 

显示留言记录

保存并显示新的留言

我们来验证一下效果吧

所有代码

留言板所有代码

<!DOCTYPE html>
<html lang="en"><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="jquery-3.7.0.min.js"></script><script>//$.ajax({url: "message/getList",type: "get",success: function(messages) {if(messages != null) {for(var message of messages) {var tmp = "<div>" + message.from +"对" + message.to + "说:" + message.say +"</div>";$(".container").append(tmp);}}}});//获取留言数据function submit(){let from = $("#from").val();let to = $("#to").val();let say = $("#say").val();//不为空if(from == "" || to == "" || say == ""){return;}//保存并显示新的留言$.ajax({url: "message/public",type: "post",contentType: "application/json",data: JSON.stringify ( {"from": from,"to": to,"say": say} ),success: function(result) {if(result) {//保存成功//将留言信息显示到页面上let message = "<div>" + from +"对" + to + "说:" + say +"</div>";$(".container").append(message);//清空输入信息$("#from").val("");$("#to").val("");$("#say").val("");} else {//保存失败alert("留言失败");}}});}</script>
</body></html>

后端代码

Message对象代码

java">import lombok.Data;@Data
public class MessageInfo {private String from;private String to;private String say;
}

后端接口代码

java">import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
import java.util.List;@RestController
@RequestMapping("/message")
public class MessageController {private List<MessageInfo> list = new ArrayList<MessageInfo>();@RequestMapping(value = "/public", method = RequestMethod.POST)public Boolean publish(@RequestBody MessageInfo messageInfo) {list.add(messageInfo);System.out.println("日志: 后端存储数据成功");return true;}@RequestMapping(value = "/getList", method = RequestMethod.GET)public List<MessageInfo> getList() {System.out.println("日志: 前端获取数据成功");return list;}
}

图书管理系统

界面


约定前后端接口

我们先来写登录接口: 判断账号密码是否正确, 首先要有账号密码的属性

java">import lombok.Data;
@Data
public class UserInfo {private String username;private String password;
}

hasLength()方法: 参数为null或者长度为空, 返回false

equal()方法: 判断二个参数是否相等

java">@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/login")public String login(String username, String password) {//判断账号或密码是否为空或者长度为0, 为空或0,方法返回falseif (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return "账号或密码不能为空";}//硬编码方式if (!"zhangsan".equals(username) || !"123456".equals(password)) {return "账号或密码错误";}return "";}
}

 图书列表: getList接口

我们通过链表存储图书, 先创建图书的属性

状态码, 因为图书状态只有可借阅和不可借阅两种情况, 所以可以用数字代替中文, 企业开发用状态码, 显示成借阅和不可借阅是前端的事情

java">@Data
public class BookInfo {private Integer bookId;  // 图书idprivate String bookName; // 书名private String author;   // 作者private Integer number;  // 数量private BigDecimal price;// 定价private  String publishName;// 出版社private Integer status;  // 状态码, 因为图书状态只有可借阅和不可借阅两种情况, 企业开发用状态码, 变成中文是前端的事情private String statusCN; // (可有可无)状态码对应的中文, 现在我们写项目前后端都是自己, 需要显示一下
}

getList接口, 以及接口测试

java">package com.white.demo;import org.springframework.util.StringUtils;
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;@RestController
@RequestMapping("/book")
public class BookController {private List<BookInfo> list = new ArrayList<BookInfo>();@RequestMapping("/getList")public List<BookInfo> getList() {list = mockData(); //mock测试(模拟测试)return list;}private List<BookInfo> mockData() {  //mock测试(从数据库调用数据进行测试),for (int i = 0; i < 15; i++) {   //由于咱们没数据库, 所以使用mockData方法模拟数据库BookInfo bookInfo = new BookInfo();bookInfo.setBookId(i);bookInfo.setBookName("书名: " + i);bookInfo.setAuthor("作者: " + i);bookInfo.setNumber(3*i+1);bookInfo.setPrice(new BigDecimal(new Random().nextInt(100)));bookInfo.setPublishName("出版社: " + i);bookInfo.setStatus(i%2==0?1:2);bookInfo.setStatusCN(i%2==0?"可借阅":"不可借阅");list.add(bookInfo);}return list;}}

使用postman测试

前端页面

我们要实现从后端拿到数据放到前端, 通过ajax拿到数据后, 如何展示页面呢?

使用HTML的话, 只能一个一个的拼接

拼接方法: 

1. 先只写一对 单引号'' 

例如: 

var tmp = '<tr>123<tr>';

2. 然后把123替换成一对 单引号, 变成这样

tmp = '<tr>''<tr>';     (中间两个单引号)

3.在中间两个单引号之间加上两个+号, 变成这样

tmp = '<tr>'++'<tr>';

4. 再两个 + 号中间填上属性变量即可

tmp = '<tr>'+要替换的变量+'<tr>';

比如这种形式: tmp = '<tr>'+bookId+'<tr>';

拼接结果就是前面两个单引号是一对

根据上述方法, 可以得到以下界面

运行结果

 应用分层(重点)

应用分层是一种设计架构, 将代码分为多个独立的层次, 可以提高代码的可阅读性, 可维护性、可扩展性和灵活性, 并且代码出现问题非常容易找到出错的部分

应用三层一般分为三层: 表现层(Controller), 业务逻辑层(Service), 数据层(Dao), 有时候还会加入实体类层(model, pojo, DTO, VO.....)

表现层(Controller)

作用: 比如用户登录例子, 表现层获取用户的输入(如用户名和密码),将这些数据传递给业务逻辑层

业务逻辑层(Service)

作用: 对用户登录的账户密码进行效验的代码属于业务逻辑层, 效验成功, 调用数据层的数据, 将页面展示出来

数据层(Dao)

作用: 接收业务逻辑层, 存储数据, 或者返回数据给业务逻辑层, 数据层一般不会调用表现层, 业务逻辑层, 

三者调用关系

表现层:创建一个业务逻辑层对象,逻辑层去执行获取的操作
业务逻辑层:创建一个数据层对象,通过数据层对象调用数据库中的数据
数据层:存储数据,提供一个接口方法,让业务逻辑层调用

那我们现在来对代码进行分层吧

首先创建4个包, 用于待会存放分层的代码

我们来对这串代码进行应用分层

表现层:创建一个业务逻辑层对象,逻辑层去执行获取的操作

业务逻辑层:创建一个数据层对象,通过数据层对象调用数据库中的数据

数据层:存储数据,提供一个接口方法,让业务逻辑层调用

美图分享


http://www.ppmy.cn/ops/137996.html

相关文章

第三百三十七节 JavaFX教程 - JavaFX矩形椭圆

JavaFX教程 - JavaFX矩形椭圆 avaFX Shape类定义了常见的形状&#xff0c;如线&#xff0c;矩形&#xff0c;圆&#xff0c;Arc&#xff0c;CubicCurve&#xff0c;Ellipse和QuadCurve。 在场景图上绘制矩形需要宽度&#xff0c;高度和左上角的&#xff08;x&#xff0c;y&…

Linux服务器CentOS操作系统运维用ntp和crontab同步网络时间

文章目录 一、概要描述二、具体操作1.查看服务器的时区2.安装ntp3.设置定时更新任务 一、概要描述 我们有台服务器&#xff0c;每隔3个月左右&#xff0c;时间会慢上超过1分钟&#xff0c;结果导致请求时间戳滞后&#xff0c;请求失败。 在经过1s钟的思索之后&#xff0c;想出…

创建型模式-建造者模式

建造者模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;用于将一个复杂对象的构建过程与其表示分离&#xff0c;使得相同的构建过程可以创建不同的表示。 核心思想 将复杂对象的创建步骤分解开来&#xff0c;通过一步步的构建来完成对象的创建&a…

SQLServer如何导入数据库

环境&#xff1a; 操作系统&#xff1a;win11 数据库&#xff1a;SQL Server2019 数据库文件&#xff1a;先前备份的.bak文件。 前提&#xff1a;一定要切换到超管用户&#xff0c;如默认的sa用户&#xff0c;不然无权访问。 右键单击数据库&#xff0c;点击“还原数据库”。 …

RVO动态避障技术方案介绍

原文&#xff1a;RVO动态避障技术方案介绍 - 哔哩哔哩 我们在开发游戏的时候经常会遇到这样的问题&#xff0c;当我们寻路的时候&#xff0c;其它人也在寻路&#xff0c;如何避免不从其它人的位置穿过。这个叫做动态避障&#xff0c;目前主流的解决方案就是RVO。本节我们来介绍…

网络安全中的数据科学如何重新定义安全实践?

组织每天处理大量数据&#xff0c;这些数据由各个团队和部门管理。这使得全面了解潜在威胁变得非常困难&#xff0c;常常导致疏忽。以前&#xff0c;公司依靠 FUD 方法&#xff08;恐惧、不确定性和怀疑&#xff09;来识别潜在攻击。然而&#xff0c;将数据科学集成到网络安全中…

postman中获取随机数、唯一ID、时间日期(包括当前日期增减)截取指定位数的字符等

在Postman中&#xff0c;您可以使用内置的动态变量和编写脚本的方式来获取随机数、唯一ID、时间日期以及截取指定位数的字符。以下是具体的操作方法&#xff1a; 一、postman中获取随机数、唯一ID、时间日期&#xff08;包括当前日期增减&#xff09;截取指定位数的字符等 获取…

week 6 - SQL Select II

Overview 1. Joins 包括交叉连接&#xff08;Cross&#xff09;、内连接&#xff08;Inner&#xff09;、自然连接&#xff08;Natural&#xff09;、外连接&#xff08;Outer&#xff09; 2. ORDER BY to produce ordered output 3. 聚合函数&#xff08;Aggregate Functio…