SpringBoot前后端交互实战案例:加法计算器与用户登录

3.1 加法计算器

需求:输⼊两个整数,点击"点击相加"按钮,计算结果显示

3.1.1 准备工作

创建SpringBoot创建:引入SpringWeb依赖,把前端页面放在项目中

3.1.2 约定前后端交互接口

概念介绍

约定 "前后端交互接口" 是进行 Web 开发中的关键环节。接口又叫 API(Application Programming Interface),我们一般讲到接口或者 API,指的都是同一个东西。是指应用程序对外提供的服务的描述,用于交换信息和执行任务(与 JavaSE 阶段学习的 [类和接口] 中的接口是两回事)。简单来说,就是允许客户端给服务器发送哪些 HTTP 请求,并且每种请求预期获取什么样的 HTTP 响应。

现在 "前后端分离" 模式开发,前端和后端代码通常由不同的团队负责开发。双方团队在开发之前,会提前约定好交互的方式。客户端发起请求,服务器提供对应的服务。服务器提供的服务种类有很多,客户端按照双方约定指定选择哪一个服务。

接口,其实也就是我们前面网络模块讲的的 "应用层协议"。把约定的内容写在文档上,就是 "接口文档",接口文档也可以理解为是 应用程序的 "操作说明书"。

比如儿童电话玩具

按 1: 响应儿歌按 2: 响应钢琴乐曲按 3: 安抚睡眠按 4: 交通工具音效......等等

但是这些操作说明,如果没有一个文档来说明,用户就不太清楚哪个按键对应哪个,所以商品一般会带一个说明书这些按键,就是接口.这个 "说明书", 就是 应用程序的 "接口文档"

在项目开发前,根据需求先约定好前后端交互接口,双方按照接口文档进行开发.

接口文档通常由服务提供方来写,交由服务使用方确认,也就是客户端.接口文档一旦写好,尽量不要轻易改.如若需要改变,必须要通知另一方知晓.

需求分析

加法计算器功能,对两个整数进行相加,需要客户端提供参与计算的两个数,服务端返回这两个整数计算的结果

需求分析

加法计算器功能,对两个整数进行相加,需要客户端提供参与计算的两个数,服务端返回这两个整数计算的结果

基于以上分析,我们来定义接口

接口定义

1 请求路径: calc/sum23 请求方式: GET/POST45 接口描述:计算两个整数相加

请求参数:
参数名 类型 是否必须 备注
num1 Integer 参与计算的第一个数
num2 Integer 参与计算的第二个数

示例: num1=5&num2=3

响应数据:

1 Content-Type: text/html23 响应内容:计算机计算结果: 8

服务器给浏览器返回计算的结果

3.1.3 服务器代码

java 复制代码
@RestController
@RequestMapping("/calc")
public class CalcController {

    @RequestMapping("/sum")
    public String sum(Integer num1,Integer num2){
        Integer sum = num1+num2;
        return "<h1>计算机计算结果: "+sum+"</h1>";
    }
}

3.1.4 调整前端页面代码

添加访问 url 和请求方式

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

3.1.5 运行测试

启动服务,运行并测试

3.2 用户登录

需求:用户输入账号和密码,后端进行校验密码是否正确

  1. 如果不正确,前端进行用户告知
  2. 如果正确,跳转到首页。首页显示当前登录用户
  3. 后续再访问首页,可以获取到登录用户信息

3.2.1 约定前后端交互接口

需求分析

对于后端开发人员而言,不涉及前端页面的展示,只需要提供两个功能

  1. 登录页面:通过账号和密码,校验输入的账号密码是否正确,并告知前端
  2. 首页:告知前端当前登录用户,如果当前已有用户登录,返回登录的账号,如果没有,返回空
接口定义
1. 校验接口
复制代码
1 请求路径: /user/login
2 
3 请求方式: POST
4 
5 接口描述: 校验账号密码是否正确

请求参数:

参数名 类型 是否必须 备注
userName String 校验的账号
password String 校验的密码

响应数据:

java 复制代码
 Content-Type: text/html
 
响应内容:
 true //账号密码验证成功
 false//账号密码验证失败
2. 查询登录用户接口
java 复制代码
 请求路径: /user/getLoginUser
 
 请求方式: GET
 
 接口描述: 查询当前登录的用户
2. 查询登录用户接口
java 复制代码
请求路径: /user/getLoginUser

 请求方式: GET
 
 接口描述: 查询当前登录的用户

**请求参数:**无

响应数据:

java 复制代码
1 Content-Type: text/html
2 
3 响应内容:
4 zhangsan

返回当前登录的用户

3.2.2 实现服务器端代码

1. 校验接口
java 复制代码
import jakarta.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class LoginController {
    /**
     * 校验用户账号和密码是否正确
     * @param userName
     * @param password
     * @param session
     * @return
     */
    @RequestMapping("/login")
    public boolean login(String userName, String password, HttpSession session)
    {
        //账号或密码为空
        if(!StringUtils.hasLength(userName) ||
                !StringUtils.hasLength(password)){
            return false;
        }
        //校验账号密码是否正确
        //理论上应该从数据库中获取账号密码,校验是否正确,但当前还没讲数据库的操作,先把账号密码固定写死
        if (!"zhangsan".equals(userName) || !"123456".equals(password)){
            return false;
        }
        //密码验证成功,把用户名存储在Session中
        session.setAttribute("userName",userName);
        return true;
    }
}
注意事项:setAttribute()
一、"存密码 + 对应读取" 的完整逻辑

以你的登录场景为例:

  1. 存储密码 :登录成功后,用固定 Key loginPwd 把密码存到 Session 里

    javascript 复制代码
    // 登录接口中存储:Key是"loginPwd",Value是用户输入的密码(比如"123456")
    session.setAttribute("loginPwd", password);
  2. 读取密码 :后续需要密码时,用同一个 Key loginPwd 从 Session 中读取

    javascript 复制代码
    // 其他接口中读取:通过Key"loginPwd"获取对应的密码值
    String savedPwd = (String) session.getAttribute("loginPwd");
二、为什么用 "固定 Key" 就能对应上?

Session 是键值对(Key-Value)映射,就像你用 "标签(Key)" 给物品(Value)做标记:

  • 存的时候,给密码贴一个固定标签 loginPwd
  • 取的时候,只要找这个标签 loginPwd,就能拿到对应的密码 ------ 这就是 "对应上" 的核心逻辑。

示例:

如果不用固定 Key(即不加双引号,用userName这类变量当 Key),确实需要保证存储和读取时userName的值完全一致且同一变量值只能存一次

第一次存的是lisi,到匹配的时候必须是一样的

StringUtils.hasLength()是 Spring 提供的一个工具方法,判断字符串是否有值字符串为 null 或者 "" 时,返回 false, 其他返回 true

java 复制代码
public static boolean hasLength(@Nullable String str) {
    return str != null && !str.isEmpty();
}
2. 查询登录用户接口
java 复制代码
/**
 * 返回登录用户信息
 * @param session
 * @return
 */
@RequestMapping("/getLoginUser")
public String getLoginUser(HttpSession session){
    //从Session中获取用户登录信息
    String userName = (String)session.getAttribute("userName");
    //如果用户已经登录,则直接返回用户登录
    if (StringUtils.hasLength(userName)){
        return userName;
    }
    return "";
}

3.2.3 调整前端页面代码

1. 调整登录页面 login.html

对于前端而言,当点击登录按钮时,需要把用户输入的信息传递到后端进行校验,后端校验成功,则跳转到首页 index.html, 后端校验失败,则直接弹窗

java 复制代码
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
    function login() {
        $.ajax({
            type: "post",
            url: "/user/login",
            data: {
                "userName": $("#userName").val(),
                "password": $("#password").val()
            },
            success: function (result) {
                if (result) {
                    location.href = "/index.html"
                } else {
                    alert("账号或密码有误.");
                }
            }
        });
    }
</script>

页面跳转的三种方式:

  1. window.location.href = "book_list.html";
  2. window.location.assign("book_list.html");
  3. window.location.replace("book_list.html");

以上写法,通常把 "window." 省略,比如 window.location.href = "book_list.html"; 写成 location.href = "book_list.html";三者区别参考:location.assign ()、location.href、location.replace (url) 的不同 - 阿里云开发者社区

2. 调整首页代码

首页代码比较简单,只显示当前登录用户即可.当前登录用户需要从后端获取,并显示到前端

java 复制代码
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
    $.ajax({
        type: "get",
        url: "/user/getLoginUser",
        success: function (result) {
            $("#loginUser").text(result);
        }
    });
</script>

3.2.4运行测试

多次刷新http://127.0.0.1:8080/index.html 发现依然可以获取到登录⽤⼾

如果重启服务器,则登录人显示:空

缓存问题;

我们在测试代码的时候我们肯定是先进行使用postman进行测试接口的正确性,如果我们确认了后端是没有问题的,我们就可以看看是不是前端的问题。

我们找到这个网页,查看代码

可是我们的代码是这个样的

与他写的这个不符合,所以我们可以摁住 ctrl + F5 强制刷新游览器页面(跳过游览器的缓存)

还有另一种解决方法就是:在这个url后面加上随机的查询字符串

我们在刷新页面的时候通常会遇到灭有发生改变,这时候我们可以考虑是游览器缓存的问题

删除缓存的游览器记录即可

然后再点击刷新,就会出现了

加随机查询字符串为什么可以预防呢?

判断 URL 是否相同------ 如果 URL 完全一致,浏览器会直接返回本地缓存的结果;如果 URL 不同(哪怕只是多了一个无关的查询参数),浏览器会认为是 "新请求",重新向服务器发起请求,从而拿到最新的资源 / 接口数据。

方式 1:拼接时间戳(最常用,简单无重复)
javascript 复制代码
$.ajax({
  type: "post",
  // 拼接时间戳:new Date().getTime() 生成当前毫秒数,每次都不同
  url: "userLogin/login2?t=" + new Date().getTime(), 
  data: {
    userName: userName,
    password: password
  },
  success: function (result) {
    // 原有逻辑...
  },
  error: function (err) {
    // 原有逻辑...
  }
});
方式 2:拼接随机数(效果一样)
复制代码
url: "userLogin/login2?random=" + Math.random(), 
场景 是否适合用这个方法 补充说明
前端 JS/CSS/HTML 静态资源缓存 ✅ 非常适合 比如 <script src="xxx.js?t=123">
AJAX 接口请求缓存 ✅ 适合 解决接口返回旧数据的问题
Postman 测试接口 ❌ 没必要 Postman 默认不会缓存请求结果
后端接口本身的逻辑 ❌ 不影响 随机参数只是 "防缓存标识",后端不会解析(也不需要解析)
方法三:临时方法 勾选「Disable cache」(禁用缓存)
  1. 打开你的登录页面,按 F12 或右键→「检查」,调出开发者工具
  2. 切换到 Network(网络)标签页;
  3. 在标签页的顶部 / 底部,找到并勾选「Disable cache」(禁用缓存)选项(部分版本显示为「禁用缓存 (停用开发者工具后失效)」)。
  4. 勾选后,只要开发者工具不关闭,浏览器刷新页面 / 发起 AJAX 请求时,不会使用本地缓存,会直接从服务器获取最新的资源(HTML/JS/CSS)和接口数据;
  5. 一旦关闭开发者工具,或者取消勾选,浏览器就会恢复正常的缓存逻辑。
相关推荐
前端小天才2 小时前
element-ui图标偶现乱码问题的原因和修复方法
开发语言·ui·rust
Biehmltym2 小时前
【AI】01开发环境:Conda_python包/环境管理,10分钟上手
开发语言·python·conda
毕设源码-邱学长2 小时前
【开题答辩全过程】以 基于SpringBoot的专业分流系统为例,包含答辩的问题和答案
java·spring boot·后端
java硕哥2 小时前
Spring与SpringBoot的重要接口及核心概念
java·spring boot·spring
小镇学者2 小时前
【golang】goland使用多版本go sdk的方法
开发语言·后端·golang
lynnlovemin2 小时前
Java技术研发年度深度总结:从架构优化到工程实践的破局之路
java·开发语言·架构·年度总结
袁气满满~_~2 小时前
Python练习
开发语言·python
Macbethad2 小时前
Jenkins自动化持续集成技术报告
开发语言
yongche_shi2 小时前
第八十九篇:CAP理论、BASE理论在系统设计中的应用
开发语言·python·面试宝典·cap理论·base理论