Java SpringMVC(二) --- 响应,综合性练习

文章目录

SpringMVC

响应

返回静态页面

  1. 返回静态页面
    1.元注解:可以被其他注解使用的叫做元注解
    @Target:表示注解修饰的对象
    {ElementType.TYPE}表示可以修饰类和接口
    @Documnted:文档,比如jdk有很多的注释,表示要不要保留这些注释

2.@RestController包括:@Controller和@ResponseBody
现在@RestController被用来返回数据了

3.@Controller的作用:告诉Spring,帮我们管理这个代码,我们后续访问时,才能访问到,才能使用我们写的代码,不写这个注解,Spring是不管这个代码的

注解的生命周期:

举个例子,写一个简单的静态页面(会打印index.html文件上的内容):

如果有多个注解,注解的顺序是不影响结果的

java 复制代码
@RequestMapping("/return")
//@RestController // 返回数据
@Controller // 返回视图
public class ReturnController {
    @RequestMapping("/index")
    public String returnIndex(){
        // 使用@RestController时返回数据
        // 这时是返回这个数据,不是返回这个页面
        return "/index.html";
    }
}

返回数据@Responsebody

  1. @Responsebody可以修饰类,也可以修饰方法,修饰类的时候,表示这个类下的所有方法,返回的均为数据
    修饰方法时,表示该方法返回的是数据
  2. 如果一个类中的所有方法返回的都是数据,我们可以把这个注解加在类上

返回一个数据

java 复制代码
@ResponseBody
    @RequestMapping("/returnData")
    public String returnData(){
        return "返回的是一个数据";
    }

返回html的代码片段

  1. 返回html的代码片段
java 复制代码
// 返回代码片段
 @ResponseBody
   @RequestMapping("/returnHtml")
   public String returnHtml(){
       return "<h1>这是一个代码</h1>";
   }


返回一个JSON

  1. 返回一个JSON
java 复制代码
 // 返回的是一个json对象
    @ResponseBody
    @RequestMapping("/returnJson")
    public Person returnJson(){
        Person person = new Person();
        person.setId(1);
        person.setAge(5);
        person.setName("zhangsan");
        return person;
    }
  1. 返回一个Map对象
java 复制代码
// 返回的是一个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格式,所以说返回的类型会被自动设置

设置状态码

  1. 状态码不影响页面的展示(显示的还是代码返回的内容,只是使用fiddler抓包时看到响应是401)
java 复制代码
@ResponseBody
    @RequestMapping("/returnStaus")
    public String returnStaus(HttpServletResponse response){
        // 401通常表示没有登录
       response.setStatus(401);

        return "设置状态码";
    }

设置Header

  1. RequestMapping

    method用来设置(限制)请求的方式

    下面这些方式通常都不会使用,限制的太多了

  2. 使用produces来设置Header返回的类型

java 复制代码
// 通过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. 对上面内容的总结:

综合性练习

  1. 练习前端和后端的交互过程,请求,响应的处理

1. 加法计算器

  1. 前端代码(calc.html):
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>
  1. 后端代码:
java 复制代码
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;
    }
}
  1. 如果报错了,该如何定位错误位置:

1.先定位前端还是后端问题

通过日志

(1) 前端:F12 看控制台

(2) 后端:接口,控制台日志

检测请求是否到达后端的方法:

(1) 可以进行抓包,看请求的路径

(2) 可以在后端进行打印信息,如果请求到了就会打印信息,否则不会打印信息(打印日志)

(3) 后端可以通过url或者是postman进行接口的测试(测试接口)

测试接口:


2. 用户登陆

  1. 用户登录时,前端存在缓存问题,还是显示之前的页面信息,那么我们该如何清理缓存?

显示的不是登录后,用户的信息,而是显示之前这个html中的信息

可以点击maven中的clean清除里面的缓存,直接运行代码里面就包含了打包的工作,浏览器中也是存在缓存的,可能还需要清理浏览器中的缓存(清除浏览器的记录就可以了)

  1. 写代码之前做的工作:

  2. 为什么登录时要设置Session的信息?

  3. 可以先测试后端的接口(后端的接口和前端的代码是没有关系的),然后再写前端的代码

  4. 前端的代码:

登录的逻辑:

login.html

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

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>
  1. 后端的代码:
java 复制代码
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,就不要创建一个Session
        HttpSession session = request.getSession(false);
        String usename = null;
        if(session != null) {
            usename = (String) session.getAttribute("usename");
        }
        return usename;
    }
}

展示效果:

登录后进行跳转页面:

项目如何debug

  1. 先打一个断点
  2. 然后点击这个小虫子,进行启动代码

断点打成功,会有一个√号

  1. 后面步骤的调试项目和我们平常调试代码是一样的

3. 留言板

  1. 前端没有保存数据的功能,后端把数据保存下来(保存在内存,数据库,文件中等等...)
    为了让留言板刷新内容不会消失
  2. 接口定义
    (1) 提交留言(给服务器)
    /message/publish
    参数:MessageInfo(from,to,message)
    返回结果:true/false

(2) 查看所有留言(后端显示所有留言)

/message/getMessageList

参数:无

返回结果:List< MessageInfo >

  1. lombok:工具包
    (1) 作用:如果一个类中有许多的变量,都需要get和set方法来设置,那就会很麻烦,这时使用lombok,就可以不用写这些get和set方法了

(2) 对于不熟悉的包,如何选择maven版本,建议选择次新版本(使用频率比较高的版本)

(3) @Data这个注解帮我们写了get方法和set方法,这个工具可以针对所有属性写get和set方法,也可以单独对某个属性写get和set方法

单独对某个属性写get和set方法

SpringBoot帮我们管理了这些包,所以应该集成了这些包,安装一个插件就可以使用插件导入这个包了,就不需要从Maven仓库中导入这个包了


lombok

  1. lombok中的一些注解

  2. @ToString:自动帮我们写了ToString方法

留言板

  1. 为什么前端校验之后,后端还需要校验?
    可能不是通过前端页面发起的请求,可能是一个非法的请求,这是最重要的原因

VSCode格式化:alt + shift + F,进行代码的对齐

  1. 前端代码:

messagewall.html

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. 拼接节点的 html
                    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. 拼接节点的 html
                        var divE = "<div>" + from + "对" + to + "说:" + say + "</div>";
                        //3. 把节点添加到页面上 
                        $(".container").append(divE);

                        //4. 清空输入框的值
                        $('#from').val("");
                        $('#to').val("");
                        $('#say').val("");
                    } else {
                        //添加失败
                        alert("留言发布失败");
                    }
                }
            });



        }

    </script>
</body>

</html>
  1. 后端代码:

MessageController:

java 复制代码
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. 进行参数的校验
        //    hasLength表示没有为空
        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:

java 复制代码
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. 目标(这里不是完全体的需求,后面会继续完善的):

  2. 定义前后端交互接口

    (1) 登录

    url:/user/login

    参数:useName = ? & password = ?

    响应:true / false

(2) 图书列表展示

url:/book/getBookList

参数:无

响应:List< BookInfo >

  1. 后端代码:

图书类:BookInfo

java 复制代码
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:

java 复制代码
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只要一个为空就返回false
         if(!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:

java 复制代码
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;
    }
}

展示效果:

应用分层

  1. 三层架构
    dao/db 通常指的是数据访问层中与数据库直接相关的组件
    db中就是数据库相关的操作
  2. 代码中数据分层的表现:
  3. 那么这些代码的包也可以进行分层
    model存的是数据对象(实体类)
相关推荐
程序员 Harry4 小时前
深度解析:使用ZIP流式读取大型PPTX文件的最佳实践
java
@。1244 小时前
对于灰度发布(金丝雀发布)的了解
开发语言·前端
wxweven4 小时前
校招面试官揭秘:我们到底在寻找什么样的技术人才?
java·面试·校招
陈陈爱java5 小时前
新知识点背诵
java
失散135 小时前
分布式专题——39 RocketMQ客户端编程模型
java·分布式·架构·rocketmq
泽02025 小时前
Linux之环境变量
java·linux·redis
hsjkdhs5 小时前
C++之多层继承、多源继承、菱形继承
开发语言·c++·算法
Full Stack Developme5 小时前
Python Redis 教程
开发语言·redis·python
程序媛徐师姐5 小时前
Java基于SpringBoot的茶叶商城系统,附源码+文档说明
java·spring boot·java springboot·茶叶商城系统·java茶叶商城系统·茶叶·java茶叶商城