Java SpringMVC(二) --- 响应,综合性练习
文章目录
- SpringMVC
- 响应
- 返回静态页面
- 返回数据@Responsebody
- 返回html的代码片段
- 返回一个JSON
- 设置状态码
- 设置Header
- 总结
- 综合性练习
- 1. 加法计算器
- 2. 用户登陆
- 项目如何debug
- 3. 留言板
- lombok
- 留言板
- 4. 图书管理系统
- 应用分层
SpringMVC
响应
返回静态页面
- 返回静态页面
1.元注解:可以被其他注解使用的叫做元注解
@Target:表示注解修饰的对象
{ElementType.TYPE}表示可以修饰类和接口
@Documnted:文档,比如jdk有很多的注释,表示要不要保留这些注释
2.@RestController包括:@Controller和@ResponseBody
现在@RestController被用来返回数据了
3.@Controller的作用:告诉Spring,帮我们管理这个代码,我们后续访问时,才能访问到,才能使用我们写的代码,不写这个注解,Spring是不管这个代码的
注解的生命周期:
举个例子,写一个简单的静态页面(会打印index.html文件上的内容):
如果有多个注解,注解的顺序是不影响结果的
@RequestMapping("/return")
//@RestController // 返回数据
@Controller // 返回视图
public class ReturnController {@RequestMapping("/index")public String returnIndex(){// 使用@RestController时返回数据// 这时是返回这个数据,不是返回这个页面return "/index.html";}
}
返回数据@Responsebody
- @Responsebody可以修饰类,也可以修饰方法,修饰类的时候,表示这个类下的所有方法,返回的均为数据
修饰方法时,表示该方法返回的是数据 - 如果一个类中的所有方法返回的都是数据,我们可以把这个注解加在类上
返回一个数据
@ResponseBody@RequestMapping("/returnData")public String returnData(){return "返回的是一个数据";}
返回html的代码片段
- 返回html的代码片段
// 返回代码片段@ResponseBody@RequestMapping("/returnHtml")public String returnHtml(){return "<h1>这是一个代码</h1>";}
返回一个JSON
- 返回一个JSON
// 返回的是一个json对象@ResponseBody@RequestMapping("/returnJson")public Person returnJson(){Person person = new Person();person.setId(1);person.setAge(5);person.setName("zhangsan");return person;}
2. 返回一个Map对象
// 返回的是一个json对象@ResponseBody@RequestMapping("/returnJson")public Person returnJson(){Person person = new Person();person.setId(1);person.setAge(5);person.setName("zhangsan");return person;}@ResponseBody@RequestMapping("/returnMap")public Map<String,String> returnMap(){Map<String,String> map = new HashMap<>();map.put("k1","v1");map.put("k2","v2");map.put("k3","v3");return map;}
返回一个对象时,会自动帮我们设置为json格式,所以说返回的类型会被自动设置
设置状态码
- 状态码不影响页面的展示(显示的还是代码返回的内容,只是使用fiddler抓包时看到响应是401)
@ResponseBody@RequestMapping("/returnStaus")public String returnStaus(HttpServletResponse response){// 401通常表示没有登录response.setStatus(401);return "设置状态码";}
设置Header
-
RequestMapping
method用来设置(限制)请求的方式
下面这些方式通常都不会使用,限制的太多了
-
使用produces来设置Header返回的类型
// 通过produces改变返回类型@ResponseBody@RequestMapping(value = "/r1",produces = "application/json; charset=utf-8")public String r1(HttpServletResponse response){// 可以设置header// header是键值对形式的response.setHeader("myhead","myhead");return "{'OK',1}";}
总结
- 对上面内容的总结:
综合性练习
- 练习前端和后端的交互过程,请求,响应的处理
1. 加法计算器
- 前端代码(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){// 这就是打印日志System.out.println("--------------------sum");Integer sum = num1 + num2;return "计算的结果为: " + sum;}
}
3. 如果报错了,该如何定位错误位置:
1.先定位前端还是后端问题
通过日志
(1) 前端:F12 看控制台
(2) 后端:接口,控制台日志
检测请求是否到达后端的方法:
(1) 可以进行抓包,看请求的路径
(2) 可以在后端进行打印信息,如果请求到了就会打印信息,否则不会打印信息(打印日志)
(3) 后端可以通过url或者是postman进行接口的测试(测试接口)
测试接口:
2. 用户登陆
- 用户登录时,前端存在缓存问题,还是显示之前的页面信息,那么我们该如何清理缓存?
显示的不是登录后,用户的信息,而是显示之前这个html中的信息
可以点击maven中的clean清除里面的缓存,直接运行代码里面就包含了打包的工作,浏览器中也是存在缓存的,可能还需要清理浏览器中的缓存(清除浏览器的记录就可以了)
-
写代码之前做的工作:
-
为什么登录时要设置Session的信息?
-
可以先测试后端的接口(后端的接口和前端的代码是没有关系的),然后再写前端的代码
-
前端的代码:
登录的逻辑:
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() {console.log("登录...");$.ajax({url: "/user/login",type: "post",data:{"userName": $("#userName").val(),"password": $("#password").val()},success:function(result){if(result){location.href = "/index.html";// location.assign();}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:"get",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){// 1. 检验输入的用户名和密码是合法的(校验参数的合法性)
// if(usename == null || usename.length() == 0 || password == null ||
// password.length() == 0){
// return false;
// }// Spring也提供了一种方式检验用户名和密码是否和法的// StringUtils.hasLength()检验字符串是否有长度if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){return false;}// 2. 进行用户名和密码的校验if("admin".equals(userName) && "admin".equals(password)){// 设置Session,存储用户这次登录的信息session.setAttribute("usename","admin");return true;}return false;}// 第一种写法:// 如果登录成功,返回登录的用户的信息
// @RequestMapping("getUserInfo")
// // HttpSession session 没有session,会自动创建一个session
// public String getUserInfo(HttpSession session){
// // 从Session中获取用户的登录名
// String usename = (String)session.getAttribute("usename");
//
// return usename;
// }// 第二种写法:@RequestMapping("/getUserInfo")public String getUserInfo(HttpServletRequest request){// 从Session中获取用户的登录名// 没有Session,就不要创建一个SessionHttpSession session = request.getSession(false);String usename = null;if(session != null) {usename = (String) session.getAttribute("usename");}return usename;}
}
展示效果:
登录后进行跳转页面:
项目如何debug
- 先打一个断点
- 然后点击这个小虫子,进行启动代码
断点打成功,会有一个√号
3. 后面步骤的调试项目和我们平常调试代码是一样的
3. 留言板
- 前端没有保存数据的功能,后端把数据保存下来(保存在内存,数据库,文件中等等…)
为了让留言板刷新内容不会消失
- 接口定义
(1) 提交留言(给服务器)
/message/publish
参数:MessageInfo(from,to,message)
返回结果:true/false
(2) 查看所有留言(后端显示所有留言)
/message/getMessageList
参数:无
返回结果:List< MessageInfo >
- lombok:工具包
(1) 作用:如果一个类中有许多的变量,都需要get和set方法来设置,那就会很麻烦,这时使用lombok,就可以不用写这些get和set方法了
(2) 对于不熟悉的包,如何选择maven版本,建议选择次新版本(使用频率比较高的版本)
(3) @Data这个注解帮我们写了get方法和set方法,这个工具可以针对所有属性写get和set方法,也可以单独对某个属性写get和set方法
单独对某个属性写get和set方法
SpringBoot帮我们管理了这些包,所以应该集成了这些包,安装一个插件就可以使用插件导入这个包了,就需要从Maven仓库中导入这个包了
lombok
-
lombo中的一些注解
-
@ToString:自动帮我们写了ToString方法
留言板
- 为什么前端校验之后,后端还需要校验?
可能不是通过前端页面发起的请求,可能是一个非法的请求,这是最重要的原因
VSCode格式化:alt + shift + F,进行代码的对齐
- 前端代码:
messagewall.html
<!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="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. 拼接节点的 htmlvar 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. 拼接节点的 htmlvar divE = "<div>" + from + "对" + to + "说:" + say + "</div>";//3. 把节点添加到页面上 $(".container").append(divE);//4. 清空输入框的值$('#from').val("");$('#to').val("");$('#say').val("");} else {//添加失败alert("留言发布失败");}}});}</script>
</body></html>
- 后端代码:
MessageController:
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 java.util.ArrayList;
import java.util.List;@RequestMapping("/message")
@RestController
public class MessageController {private List<MessageInfo> messageInfos = new ArrayList<>();// 1. 提交留言@RequestMapping("/publish")public Boolean publishMessage(MessageInfo messageInfo){// 1. 进行参数的校验// hasLenght表示没有为空if(!StringUtils.hasLength(messageInfo.getFrom()) ||!StringUtils.hasLength(messageInfo.getTo())||!StringUtils.hasLength(messageInfo.getMessage())){// 只要一个为空,就不发送return false;}// 2.添加留言messageInfos.add(messageInfo);return true;}// 2. 查看留言@RequestMapping("/getMessageInfo")public List<MessageInfo> getMessageInfo(){return messageInfos;}
}
用到的类:
MessageInfo:
package com.example.demo.controller;import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;import java.util.Date;@Data
class MessageInfo {private String from;private String to;private String message;// private Date CreateTime;
}
效果展示:
4. 图书管理系统
-
目标:
-
定义前后端交互接口
(1) 登录
url:/user/login
参数:useName = ? & password = ?
响应:true / false
(2) 图书列表展示
url:/book/getBookList
参数:无
响应:List< BookInfo >
- 后端代码:
图书类:BookInfo
package com.example.book2;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;// 图书的中文状态说明
}
登录:
UserController:
package com.example.book2;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 useName, String password, HttpSession session){// 1. 校验参数// useName和password只要一个为空就返回falseif(!StringUtils.hasLength(useName) || !StringUtils.hasLength(password)){return false;}/*useName.equasl("admin"),如果useName为空时,会报空指针异常这是一个开发习惯*/// 2. 验证账号,密码是否正确if("admin".equals(useName) && "admin".equals(password)){// 账号密码正确// 存储Session,把用户名存起来session.setAttribute("useName",useName);return true;}return false;}
}
图书列表展示:
BookController:
package com.example.book2;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);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;}
}
展示效果:
应用分层
- 三层架构
- 代码中数据分层的表现
- 那么这些代码的包也可以进行分层