Spring MVC Web 开发实战:接口约定与前后端交互
一、加法计算器
1. 准备工作
首先创建一个 Spring Boot 项目,引入 spring-web 依赖。前端页面直接放在项目的静态资源目录下即可。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>计算器</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>
界面效果如下所示:
![图:计算器界面]
2. 接口约定
在前后端分离或传统 Web 开发中,接口约定是协作的基石。这里的'接口'指的是应用程序对外提供的服务描述(API),本质是客户端与服务器之间遵循的应用层协议。
它明确了三点:
- 请求规则:路径是什么?用 GET 还是 POST?参数名和类型是什么?
- 响应规范:返回的数据格式(JSON、HTML 等)及结构。
- 业务逻辑:输入什么数据,预期得到什么结果。
一旦文档确定,双方就按此并行开发,能大幅降低沟通成本和返工风险。
需求与接口分析
我们需要实现两个整数相加的功能。
- 请求路径:
/calc/sum - 请求方式:GET / POST
- 参数说明:
| 参数名 | 类型 | 是否必须 | 备注 |
|---|---|---|---|
| num1 | Integer | 是 | 第一个加数 |
| num2 | Integer | 是 | 第二个加数 |
3. 后端实现
控制器类定义如下,注意使用 @RestController 简化开发:
package com.yang.test1_19_5;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/calc")
@RestController
public class CalController {
@RequestMapping("/sum")
public String sum(Integer num1, Integer num2) {
if (num1 == null || num2 == null) {
return "输入的数据不合法";
}
Integer sum = num1 + num2;
return "计算结果:" + sum;
}
}
实际运行后,浏览器会显示计算结果:
![图:计算结果展示]
二、用户登录
1. 准备工作
同样创建 Spring Boot 项目并引入 Web 依赖。这里涉及两个页面:首页用于展示当前登录状态,登录页用于提交凭证。
首页代码:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录首页</title>
</head>
<body>
登录人:<span id="user"></span>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
// 页面加载时获取当前用户信息
$(function() {
$.get('/user/getLoginUser', function(data) {
$('#user').text(data);
});
});
</script>
</body>
</html>
登录页代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h1>用户登录</h1>
用户名:<input name="userName" type="text"><br>
密码:<input name="password" type="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() {
var userName = $('input[name="userName"]').val();
var password = $('input[name="password"]').val();
$.post('/user/login', { userName: userName, passWord: password }, function(res) {
if (res === true) {
alert('登录成功');
location.href = '/index.html'; // 跳转首页
} else {
alert('账号或密码错误');
}
});
}
</script>
</body>
</html>
2. 接口设计
校验接口
- 路径:
/user/login - 方式:POST
- 描述:验证用户名和密码
- 参数:
userName,passWord - 响应:
true(成功) /false(失败)
查询登录用户接口
- 路径:
/user/getLoginUser - 方式:GET
- 描述:获取当前 Session 中的用户信息
- 响应:用户名字符串(未登录返回空)
3. 后端实现
这里重点演示 Session 的使用。注意 request.getSession() 默认会创建新 Session,如果不需要自动创建,可以传入 false。
package com.yang.test1_20_1;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/user")
@RestController
public class UserController {
/**
* 校验用户名和密码是否正确
*/
@PostMapping("/login")
public Boolean login(String userName, String password, HttpSession session) {
if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {
return false;
}
// 模拟校验逻辑,常量放前面避免 NPE
if ("Bruce".equals(userName) && "12345".equals(password)) {
// 存储 Session
session.setAttribute("loginUser", userName);
return true;
}
return false;
}
@GetMapping("/getLoginUser")
public String getLoginUser(HttpServletRequest request) {
// 参数为 false,获取的 session 为 null 时,不会创建 session
HttpSession session = request.getSession(false);
if (session != null) {
String loginUser = (String) session.getAttribute("loginUser");
return loginUser != null ? loginUser : "";
}
return "";
}
}
运行效果如下:
![图:登录流程演示]
三、留言板
1. 准备工作
这是一个典型的前后端交互场景。前端需要展示留言列表并提交新留言。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>留言板</title>
<style>
.container { width: 350px; height: 300px; margin: 0 auto; text-align: center; }
.row { width: 350px; height: 40px; display: flex; justify-content: space-between; align-items: center; }
.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>输入后点击提交,会将信息显示下方空白处</p>
<div class="row"><span>谁:</span><input type="text" id="from"></div>
<div class="row"><span>对谁:</span><input type="text" id="to"></div>
<div class="row"><span>说什么:</span><input type="text" id="say"></div>
<input type="button" value="提交" onclick="submit()">
<!-- 留言展示区 -->
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
function submit(){
var from = $('#from').val();
var to = $('#to').val();
var say = $('#say').val();
if (from== '' || to == '' || say == '') { return; }
// 构造节点
var divE = "<div>"+from +"对" + to + "说:" + say+"</div>";
// 添加到页面
$('.container').append(divE);
// 清空输入框
$('#from').val(""); $('#to').val(""); $('#say').val("");
}
</script>
</body>
</html>
2. 接口定义
后端需要提供两个服务:保存留言和获取留言。
- 获取全部留言:
GET /message/getList- 响应:JSON 数组
[{ "from":"黑猫", "to":"白猫", "message":"喵" }] - 发表新留言:
POST /message/publish- 请求体:JSON 对象
{ "from":"黑猫", "to":"白猫", "message":"喵" }- 响应:
{"OK":1}
3. Lombok 简介
在编写实体类时,我们通常需要大量的 Getter、Setter 和构造函数。Lombok 通过注解在编译期自动生成这些代码,极大减少了样板代码。
Maven 依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
<scope>compile</scope>
</dependency>
常用注解:
| 注解 | 作用 |
|---|---|
@Getter | 自动生成 getter 方法 |
@Setter | 自动生成 setter 方法 |
@Data | 组合了 Getter, Setter, ToString, Equals, HashCode |
@NoArgsConstructor | 生成无参构造 |
@AllArgsConstructor | 生成全参构造 |
4. 后端实现
结合 MessageInfo 实体类(假设已使用 Lombok 注解),Controller 代码如下:
package com.yang.test1_21_1;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
// 假设 MessageInfo 类使用了 @Data 等注解
@RequestMapping("/message")
@RestController
public class MessageController {
List<MessageInfo> messageInfoList = new ArrayList<>();
@PostMapping("/publish")
public Boolean publish(@RequestBody MessageInfo messageInfo) {
if (!StringUtils.hasLength(messageInfo.getFrom()) ||
!StringUtils.hasLength(messageInfo.getTo()) ||
!StringUtils.hasLength(messageInfo.getMessage())) {
return false;
}
messageInfoList.add(messageInfo);
return true;
}
@GetMapping("/getList")
public List<MessageInfo> getList() {
return messageInfoList;
}
}
最终效果:
![图:留言板功能演示]


