基于Spring MVC的前后端交互案例及应用分层的实现

目录

分析程序报错的步骤

案例

一.加法计算器

二.实现用户登录

1.登录接口

2.获取用户的登录信息

三.留言板

1.接口定义

2.完成后端代码

3.测试后端代码

四.图书管理系统

1.定义接口

2.后端代码

3.测试后端代码

4.前端交互代码

应用分层

1.三层架构


分析程序报错的步骤

1.定位是前端还是后端:通过日志

1)前端:F12、看控制台

2)后端:接口、控制台

2.判断请求是否到达后端

1)后端代码在相应方法的第一行,通过打印一些标记的字符串来判断。运行后如果没有打印内容则请求没有到达后端

2)抓包查看访问的接口是否正确

3.当感觉前端代码没有错,后端却接收不到请求

测试接口:http地址中加上后端参数访问,测试后端代码是否可以实现。

4.查看缓存问题

常见的是当前端有代码覆盖后,后面新的代码运行成功却在页面显示的是覆盖前的页面,这时在Maven面板点击clean清除缓存。

案例

一.加法计算器

前端代码: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>

后端实现相加:

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){
        Integer sum=num1+num2;
        return "计算结果为:"+sum;
    }
}

二.实现用户登录

1.登录接口

/user/login

userName = ? & password = ?

接口返回:校验成功/失败

true 密码正确

false 密码错误

2.获取用户的登录信息

/user/getUserInfo

接口返回:当前登录用户的名称

前端代码

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() {
      // 测试日志 检测前端请求是否能发出(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

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>

后端代码:

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){
        //校验参数的合法性
//        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)){
            //设置session
            session.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.完成后端代码

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

由此可见后端代码正确。

前端交互主要代码:

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

java 复制代码
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)){
            //账号密码正确
            //存session
            session.setAttribute("userName",userName);
            return true;
        }
        return false;
    }
}

BookController

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

java 复制代码
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.前端交互代码

前端登录页跳转到图书列表页面主要代码:

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

前端图书列表页获取到后端自动生成的图书:

html 复制代码
 function getBookList() {
                $.ajax({
                    url:"/book/getBookList",
                    type:"get",
                    success:function(books){
                        var finalHtml="";
                        for(var book of books){
                           //根据每条记录拼接html,也就是一个tr
                           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.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强调数据和视图分离,将数据和数据处理分开,使用控制器对两者进行组合。

三层架构强调不同维度的数据处理:将交互界面、业务处理和数据库的逻辑分开。

两者可以互相转换。两者的目的都是:解耦、分层、代码复用。

相关推荐
智慧老师2 小时前
Spring基础分析13-Spring Security框架
java·后端·spring
hanbarger5 小时前
mybatis框架——缓存,分页
java·spring·mybatis
龙少95438 小时前
【深入理解@EnableCaching】
java·后端·spring
啦啦右一14 小时前
Spring Boot | (一)Spring开发环境构建
spring boot·后端·spring
荆州克莱17 小时前
mysql中局部变量_MySQL中变量的总结
spring boot·spring·spring cloud·css3·技术
zquwei17 小时前
SpringCloudGateway+Nacos注册与转发Netty+WebSocket
java·网络·分布式·后端·websocket·网络协议·spring
火烧屁屁啦17 小时前
【JavaEE进阶】初始Spring Web MVC
java·spring·java-ee
岁岁岁平安18 小时前
spring学习(spring-DI(字符串或对象引用注入、集合注入)(XML配置))
java·学习·spring·依赖注入·集合注入·基本数据类型注入·引用数据类型注入
北辰浮光18 小时前
[spring]XML配置文件标签
xml·spring
ZSYP-S18 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring