目录
分析程序报错的步骤
案例
一.加法计算器
二.实现用户登录
1.登录接口
2.获取用户的登录信息
三.留言板
1.接口定义
2.完成后端代码
3.测试后端代码
四.图书管理系统
1.定义接口
2.后端代码
3.测试后端代码
4.前端交互代码
应用分层
1.三层架构
分析程序报错的步骤
1.定位是前端还是后端:通过日志
1)前端:F12、看控制台
2)后端:接口、控制台
2.判断请求是否到达后端
1)后端代码在相应方法的第一行,通过打印一些标记的字符串来判断。运行后如果没有打印内容则请求没有到达后端
2)抓包查看访问的接口是否正确
3.当感觉前端代码没有错,后端却接收不到请求
测试接口:http地址中加上后端参数访问,测试后端代码是否可以实现。
4.查看缓存问题
常见的是当前端有代码覆盖后,后面新的代码运行成功却在页面显示的是覆盖前的页面,这时在Maven面板点击clean清除缓存。
案例
一.加法计算器
前端代码:calc.html
<!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>
后端实现相加:
package com.example.demo.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/calc")
@RestController
public class calcController {@RequestMapping("/sum")public String sum(Integer num1,Integer num2){Integer sum=num1+num2;return "计算结果为:"+sum;}
}
二.实现用户登录
1.登录接口
/user/login
userName = ? & password = ?
接口返回:校验成功/失败
true 密码正确
false 密码错误
2.获取用户的登录信息
/user/getUserInfo
接口返回:当前登录用户的名称
前端代码
login.html
<!--用户登录-->
<!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() {// 测试日志 检测前端请求是否能发出(onlick)的时候console.log("登录...")$.ajax({url:"/user/login",type:"post",data:{"userName":$("#userName").val(),"password":$("#password").val()},success:function(result){if(result){location.href ="/index2.html";}else{alert("密码错误");}}});}</script>
</body></html>
index.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="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>// 页面加载的时候就去调用后端请求$.ajax({url:"/user/getUserInfo",type:"post",success:function(userName){$("#loginUser").text(userName);}})
</script>
</body></html>
后端代码:
package com.example.demo.controller;import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;@RequestMapping("/user")
@RestController
public class UserController {@RequestMapping("/login")public Boolean login(String userName, String password, HttpSession session){//校验参数的合法性
// if(userName==null ||userName.length()==0 || password==null || password.length()==0){
// return false;
// }if(!StringUtils.hasLength(userName)||!StringUtils.hasLength(password)){return false;}//进行用户名和密码的校验if("admin".equals(userName)&&"admin".equals(password)){//设置sessionsession.setAttribute("username","admin");return true;}return false;}@RequestMapping("/getUserInfo")
// public String getUserInfo(HttpSession session){
// //从session中获取登录用户
// String username=(String) session.getAttribute("username");
// return username;public String getUserInfo(HttpServletRequest request){//从session中获取用户HttpSession session= request.getSession(false);String username=null;if(session!=null){username=(String)session.getAttribute("username");}return username;}
}
三.留言板
前端页面
现在后端需要完成的是:把数据保存下来(内存、数据库....)
1.接口定义
(1)提交留言
/message/publish
参数:MessgaeInfo(from,to,message)
返回结果:true/false
(2)查看所有留言
/message/getMessageList
参数:无
返回结果:List<MessageInfo>
后端代码:
定义一个信息类,这时我们可以使用maven自带的工具lombok自动定义set和get方法。
针对所有属性加上set和get方法,@Data放在类的外面
2.完成后端代码
package com.example.demo;import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
import java.util.List;@RequestMapping("/message")
@RestController
public class MessageController {private List<MessageInfo> messageInfos=new ArrayList<>();//发布留言@RequestMapping("/publish")public Boolean publishMassage(MessageInfo messageInfo){//进行参数校验if(!StringUtils.hasLength(messageInfo.getFrom()) ||!StringUtils.hasLength(messageInfo.getTo()) ||!StringUtils.hasLength(messageInfo.getMessage())){return false;}//添加留言messageInfos.add(messageInfo);return true;}//查看留言@RequestMapping("/getMessageInfo")public List<MessageInfo> getMessageInfo(){return messageInfos;}
}
3.测试后端代码
在postman上进行测试
publish测试
getMessageInfo测试
由此可见后端代码正确。
前端交互主要代码:
<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({url: "/message/getMessageInfo",type: "get",success: function (messages) {for (var m of messages) {//2. 构造节点var divE = "<div>" + m.from + "对" + m.to + "说:" + m.message + "</div>";//3. 把节点添加到页面上$(".container").append(divE);}}})function submit() {//1. 获取留言的内容var from = $('#from').val();var to = $('#to').val();var say = $('#say').val();if (from == '' || to == '' || say == '') {return;}//提交留言$.ajax({url: "/message/publish",type: "post",data: {"from": from,"to": to,"message": say},success: function (result) {if (result) {//留言成功//2. 构造节点var divE = "<div>" + from + "对" + to + "说:" + say + "</div>";//3. 把节点添加到页面上$(".container").append(divE);//4. 清空输入框的值$('#from').val("");$('#to').val("");$('#say').val("");} else {//留言失败alert("发布留言失败")}}});}</script>
</body>
四.图书管理系统
1.定义接口
(1)登录
url: /user/login
参数:userName=?&password=?
响应:True/False
(2)图书列表展示
url: /book/getBookList
参数:无
响应:List<BookInfo>
2.后端代码
UserController
package com.lele.book;import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpSession;@RequestMapping("/user")
@RestController
public class UserController {@RequestMapping("/login")public Boolean login(String userName, String password, HttpSession session){//校验参数if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {return false;}//if(userName.equals("admin")){}这种写法会报空指针异常//校验账号密码是否正确if("admin".equals(userName) && "admin".equals(password)){//账号密码正确//存sessionsession.setAttribute("userName",userName);return true;}return false;}
}
BookController
package com.lele.book;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("/getBookList")public List<BookInfo> getBookList(){//1.获取图书数据//2.对图书数据进行修改//3.返回数据//mock表示虚拟的假数据List<BookInfo> bookInfos=mockData();for (BookInfo bookInfo:bookInfos){if(bookInfo.getStatus()==1){bookInfo.setStatusCN("可借阅");}else {bookInfo.setStatusCN("不可借阅");}}return bookInfos;}private List<BookInfo> mockData() {//对已知的数据量,创建list时建议指定初始化的值List<BookInfo> bookInfos=new ArrayList<>(15);//随机生成15条图书数据for (int i = 0; i < 15; i++) {BookInfo bookInfo=new BookInfo();bookInfo.setId(i);bookInfo.setBookName("图书"+i);bookInfo.setAuthor("作者"+i);bookInfo.setCount(new Random().nextInt(200));bookInfo.setPrice(new BigDecimal(new Random().nextInt(100)));bookInfo.setPublish("出版社"+i);bookInfo.setStatus(i%5==0?2:1);bookInfos.add(bookInfo);}return bookInfos;}
}
BookInfo
package com.lele.book;import lombok.Data;import java.math.BigDecimal;@Data
public class BookInfo {private Integer id;private String BookName;private String author;private Integer count;private BigDecimal price;private String publish;private Integer status;//1-可借阅 2-不可借阅private String statusCN;}
3.测试后端代码
测试接口是否可以正常使用
4.前端交互代码
前端登录页跳转到图书列表页面主要代码:
function login() {$.ajax({url:"user/login",type:"post",data:{"userName":$("#userName").val(),"password":$("#password").val()},success:function(result){if(result){location.href="book_list.html";}else{alert("用户名或密码错误!");}}})location.href = "book_list.html";}
前端图书列表页获取到后端自动生成的图书:
function getBookList() {$.ajax({url:"/book/getBookList",type:"get",success:function(books){var finalHtml="";for(var book of books){//根据每条记录拼接html,也就是一个trfinalHtml +='<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.statusCN+'</td>';finalHtml +='<td> <div class="op">';finalHtml +=' <a href="book_update.html?bookId='+book.id+'">修改</a>';finalHtml +='<a href="javascript:void(0)" onclick="deleteBook('+book.id+')">删除</a>';finalHtml +='</div>';finalHtml +='</td></tr>';}console.log(finalHtml);$("tbody").html(finalHtml);}})}
应用分层
以上代码已经是非常简单的业务需求,但前后端代码的交互已经略显混乱。
MVC分层方式已经不足以满足现在的业务需求,取而代之的是三层架构。
1.三层架构
(1)表现层:接收请求,返回结果
(2)业务逻辑层:业务逻辑处理
(3)数据层:处理数据,包含数据的存储、获取(增删改查)
2.在Spring中的体现
类比上面的图书管理系统:
对图书管理系统进行三层架构:
目录分层:
调整上述代码:
MVC强调数据和视图分离,将数据和数据处理分开,使用控制器对两者进行组合。
三层架构强调不同维度的数据处理:将交互界面、业务处理和数据库的逻辑分开。
两者可以互相转换。两者的目的都是:解耦、分层、代码复用。