SpringMVC——第6章:RESTFul编程风格
一、RESTFul编程风格
1.RESTFul是什么
RESTFul是WEB服务接口
的一种设计风格。 RESTFul定义了一组约束条件和规范,可以让WEB服务接口
更加简洁、易于理解、易于扩展、安全可靠。
RESTFul对一个WEB服务接口
都规定了哪些东西?
对请求的URL格式有约束和规范
对HTTP的请求方式有约束和规范
对请求和响应的数据格式有约束和规范
对HTTP状态码有约束和规范
等 ......
REST对请求方式的约束是这样的:
查询必须发送GET请求
新增必须发送POST请求
修改必须发送PUT请求
删除必须发送DELETE请求
REST对URL的约束是这样的:
传统的URL:get请求,/springmvc/getUserById?id=1
REST风格的URL:get请求,/springmvc/user/1
传统的URL:get请求,/springmvc/deleteUserById?id=1
REST风格的URL:delete请求, /springmvc/user/1
RESTFul对URL的约束和规范的核心是:通过采用**不同的请求方式**
+ **URL**
来确定WEB服务中的资源。
RESTful 的英文全称是 Representational State Transfer(表述性状态转移)。简称REST。
表述性(Representational)是:URI + 请求方式。
状态(State)是:服务器端的数据。
转移(Transfer)是:变化。
表述性状态转移是指:通过 URI + 请求方式 来控制服务器端数据的变化。
2.RESTFul风格与传统方式对比
传统的 URL 与 RESTful URL 的区别是传统的 URL 是基于方法名进行资源访问和操作,而 RESTful URL 是基于资源的结构和状态进行操作的。下面是一张表格,展示两者之间的具体区别:
传统的 URL | RESTful URL |
---|---|
GET /getUserById?id=1 | GET /user/1 |
GET /getAllUser | GET /user |
POST /addUser | POST /user |
POST /modifyUser | PUT /user |
GET /deleteUserById?id=1 | DELETE /user/1 |
从上表中我们可以看出,传统的URL是基于动作的,而 RESTful URL 是基于资源和状态的,因此 RESTful URL 更加清晰和易于理解,这也是 REST 架构风格被广泛使用的主要原因之一。
3.RESTFul方式演示查询
RESTFul规范中规定,如果要查询数据,需要发送GET请求。
根据id查询和查询所有
测试:
4.RESTFul方式演示增加
RESTFul规范中规定,如果要进行保存操作,需要发送POST请求。
新增一个用户类
新增表单
测试:
5.RESTFul方式演示修改
RESTful编程风格中要求,修改的时候,必须提交PUT请求,怎么提交PUT请求呢?第一步:要想发送PUT请求,首先你必须是一个POST请求。(POST请求是大前提)第二步:在POST请求的表单中添加隐藏域:<!--隐藏域--><input type="hidden" name="_method" value="put">强调:name必须是 _method,value必须是put/PUT。如果你要发送delete请求的话,value写delete即可。第三步:添加一个过滤器<!--添加一个过滤器,这个过滤器是springmvc提前写好的,直接用就行了,这个过滤器可以帮助你将请求POST转换成PUT请求/DELETE请求--><filter><filter-name>hiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class></filter><filter-mapping><filter-name>hiddenHttpMethodFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
RESTFul规范中规定,如果要进行保存操作,需要发送PUT请求。
如何发送PUT请求?
第一步:首先你必须是一个POST请求。
第二步:在发送POST请求的时候,提交这样的数据:**_method=PUT**
第三步:在web.xml文件配置SpringMVC提供的过滤器:HiddenHttpMethodFilter
实践一下:
在web.xml配置
<!--隐藏的HTTP请求方式过滤器-->
<filter><filter-name>hiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping><filter-name>hiddenHttpMethodFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
测试:
6.HiddenHttpMethodFilter
HiddenHttpMethodFilter是Spring MVC框架提供的,专门用于RESTFul编程风格。 实现原理可以通过源码查看:
通过源码可以看到,if语句中,首先判断是否为POST请求,如果是POST请求,调用request.getParameter(this.methodParam)
。
可以看到this.methodParam
是_method
,这样就要求我们在提交请求方式的时候必须采用这个格式:_method=put
。获取到请求方式之后,调用了toUpperCase转换成大写了。因此前端页面中小写的put或者大写的PUT都是可以的。if语句中嵌套的if语句说的是,只有请求方式是 PUT,DELETE,PATCH的时候会创建HttpMethodRequestWrapper对象。而HttpMethodRequestWrapper对象的构造方法是这样的:
这样method就从POST变成了:PUT/DELETE/PATCH。
重点注意事项:CharacterEncodingFilter和HiddenHttpMethodFilter的顺序 细心的同学应该注意到了,在HiddenHttpMethodFilter源码中有这样一行代码:
大家是否还记得,字符编码过滤器执行之前不能调用 request.getParameter方法,如果提前调用了,乱码问题就无法解决了。因为request.setCharacterEncoding()方法的执行必须在所有request.getParameter()方法之前执行。因此这两个过滤器就有先后顺序的要求,在web.xml文件中,应该先配置CharacterEncodingFilter,然后再配置HiddenHttpMethodFilter。
7.RESTFul方式演示删除
测试:
二、使用RESTFul实现用户管理系统
1.·静态页面准备
文件包括:user.css、user_index.html、user_list.html、user_add.html、user_edit.html。代码如下:
user.css
.header {background-color: #f2f2f2;padding: 20px;text-align: center;
}ul {list-style-type: none;margin: 0;padding: 0;overflow: hidden;background-color: #333;
}li {float: left;
}li a {display: block;color: white;text-align: center;padding: 14px 16px;text-decoration: none;
}li a:hover:not(.active) {background-color: #111;
}.active {background-color: #4CAF50;
}form {width: 50%;margin: 0 auto;padding: 20px;border: 1px solid #ddd;border-radius: 4px;
}label {display: block;margin-bottom: 8px;
}input[type="text"], input[type="email"], select {width: 100%;padding: 6px 10px;margin: 8px 0;box-sizing: border-box;border: 1px solid #555;border-radius: 4px;font-size: 16px;
}button[type="submit"] {padding: 10px;background-color: #4CAF50;color: #fff;border: none;border-radius: 4px;cursor: pointer;
}button[type="submit"]:hover {background-color: #3e8e41;
}table {border-collapse: collapse;width: 100%;
}th, td {border: 1px solid #ddd;padding: 8px;text-align: left;
}th {background-color: #f2f2f2;
}tr:nth-child(even) {background-color: #f2f2f2;
}.header {background-color: #f2f2f2;padding: 20px;text-align: center;
}a {text-decoration: none;color: #333;
}.add-button {margin-bottom: 20px;padding: 10px;background-color: #4CAF50;color: #fff;border: none;border-radius: 4px;cursor: pointer;
}.add-button:hover {background-color: #3e8e41;
}
user_index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>用户管理系统</title><link rel="stylesheet" href="user.css" type="text/css"></link>
</head>
<body><div class="header"><h1>用户管理系统</h1></div><ul><li><a class="active" href="user_list.html">用户列表</a></li></ul>
</body>
</html>
user_list.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>用户列表</title><link rel="stylesheet" href="user.css" type="text/css"></link>
</head>
<body><div class="header"><h1>用户列表</h1></div><div class="add-button-wrapper"><a class="add-button" href="user_add.html">新增用户</a></div><table><thead><tr><th>编号</th><th>用户名</th><th>性别</th><th>邮箱</th><th>操作</th></tr></thead><tbody><tr><td>1</td><td>张三</td><td>男</td><td>zhangsan@powernode.com</td><td>修改删除</td></tr><tr><td>2</td><td>李四</td><td>女</td><td>lisi@powernode.com</td><td>修改删除</td></tr></tbody></table>
</body>
</html>
user_add.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>新增用户</title><link rel="stylesheet" href="user.css" type="text/css"></link>
</head>
<body><h1>新增用户</h1><form><label>用户名:</label><input type="text" name="username" required><label>性别:</label><select name="gender" required><option value="">-- 请选择 --</option><option value="1">男</option><option value="0">女</option></select><label>邮箱:</label><input type="email" name="email" required><button type="submit">保存</button></form>
</body>
</html>
user_edit.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>修改用户</title><link rel="stylesheet" href="user.css" type="text/css"></link>
</head>
<body><h1>修改用户</h1><form><label>用户名:</label><input type="text" name="username" value="张三" required><label>性别:</label><select name="gender" required><option value="">-- 请选择 --</option><option value="1" selected>男</option><option value="0">女</option></select><label>邮箱:</label><input type="email" name="email" value="zhangsan@powernode.com" required><button type="submit">修改</button></form>
</body>
</html>
代码有两处需要修改:
部署,启动服务器,测试:
2.实现用户列表
修改user_index.html中的超链接:
编写bean:User
package org.example1.bean;public class User {/*** 用户编号*/private Long id;/*** 用户名*/private String username;/*** 性别* 1表示男* 0表示女*/private Integer sex;/*** 邮箱*/private String email;public User() {}public User(Long id, String username, Integer sex, String email) {this.id = id;this.username = username;this.sex = sex;this.email = email;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", sex=" + sex +", email='" + email + '\'' +'}';}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public Integer getSex() {return sex;}public void setSex(Integer sex) {this.sex = sex;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}
}
编写UserDao,提供selectAll方法:
package org.example1.dao;import org.example1.bean.User;
import org.springframework.stereotype.Repository;import java.util.ArrayList;
import java.util.List;@Repository
public class UserDao {private static List<User> users = new ArrayList<>();static {// 类加载是初始化数据// 创建User对象User user1 = new User(1001L,"张三", 1, "zhangsan@powernode.com");User user2 = new User(1002L,"孙悟空", 1, "wukong@powernode.com");User user3 = new User(1003L,"猪八戒", 1, "bajie@powernode.com");User user4 = new User(1004L,"白骨精", 0, "bgj@powernode.com");User user5 = new User(1005L,"武松", 1, "ws@powernode.com");// 将User对象存储到List集合中users.add(user1);users.add(user2);users.add(user3);users.add(user4);users.add(user5);}/*** 查询所有的用户信息* @return 用户列表List集合*/public List<User> selectAll(){return users;}}
编写控制器UserController:
package org.example1.controller;import org.example1.bean.User;
import org.example1.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;import java.util.List;@Controller
public class UserController {@Autowiredprivate UserDao userDao;@RequestMapping(value = "/user", method = RequestMethod.GET)public String list(Model model){// 查询数据库,获取用户列表List集合List<User> users = userDao.selectAll();// 将用户列表存储到request域当中model.addAttribute("users", users);// 转发到视图return "user_list";}}
将user_list.html拷贝到thymeleaf目录下,并进行代码修改,显示用户列表:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>用户列表</title><link rel="stylesheet" th:href="@{/static/user.css}" type="text/css"></link>
</head>
<body><div class="header"><h1>用户列表</h1></div><div class="add-button-wrapper"><a class="add-button" href="user_add.html">新增用户</a></div><table><thead><tr><th>编号</th><th>用户名</th><th>性别</th><th>邮箱</th><th>操作</th></tr></thead><tbody><tr th:each="user : ${users}"><td th:text="${user.id}"></td><td th:text="${user.name}"></td><td th:text="${user.gender == 1 ? '男' : '女'}"></td><td th:text="${user.email}"></td><td><a href="">修改</a><a href="">删除</a></td></tr></tbody></table>
</body>
</html>
测试结果:
3.实现新增功能
跳转到新增页面
在用户列表页面,修改新增用户
的超链接:
将user_add.html拷贝到thymeleaf目录下,并进行代码修改如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http:www.thymeleaf.org">
<head><meta charset="UTF-8"><title>新增用户</title><link rel="stylesheet" th:href="@{/static/user.css}" type="text/css"></link>
</head>
<body><h1>新增用户</h1><form><label>用户名:</label><input type="text" name="username" required><label>性别:</label><select name="gender" required><option value="">-- 请选择 --</option><option value="1">男</option><option value="0">女</option></select><label>邮箱:</label><input type="email" name="email" required><button type="submit">保存</button></form>
</body>
</html>
在springmvc.xml文件中配置视图控制器映射
:
<mvc:view-controller path="/toSave" view-name="user_add"/>
启动服务器测试:
实现新增功能
前端页面发送POST请求,提交表单,user_add.html代码如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http:www.thymeleaf.org">
<head><meta charset="UTF-8"><title>新增用户</title><link rel="stylesheet" th:href="@{/static/user.css}" type="text/css"></link>
</head>
<body><h1>新增用户</h1><form th:action="@{/user}" method="post"><label>用户名:</label><input type="text" name="name" required><label>性别:</label><select name="gender" required><option value="">-- 请选择 --</option><option value="1">男</option><option value="0">女</option></select><label>邮箱:</label><input type="email" name="email" required><button type="submit">保存</button></form>
</body>
</html>
编写控制器UserController:
注意:保存成功后,采用重定向的方式跳转到用户列表。
编写UserDao:
注意:单独写了一个方法生成id,内部使用了Stream API,不会这块内容的可以看老杜最新发布的2024版JavaSE。
4.跳转到修改页面
修改user_list.html中修改
超链接:
<a th:href="@{'/user/' + ${user.id}}">修改</a>
编写Controller:
@GetMapping("/user/{id}")
public String toUpdate(@PathVariable("id") Long id, Model model){// 根据id查询用户信息User user = userDao.selectById(id);// 将对象存储到request域model.addAttribute("user", user);// 跳转视图return "user_edit";
}
编写UserDao:
public User selectById(Long id){return users.stream().filter(user -> user.getId().equals(id)).findFirst().get();
}
将user_edit.html拷贝thymeleaf目录下,并修改代码如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>修改用户</title><link rel="stylesheet" th:href="@{/static/user.css}" type="text/css"></link>
</head>
<body><h1>修改用户</h1><form><label>用户名:</label><input type="text" name="username" th:value="${user.name}" required><label>性别:</label><select name="gender" required><option value="">-- 请选择 --</option><option value="1" th:field="${user.gender}">男</option><option value="0" th:field="${user.gender}">女</option></select><label>邮箱:</label><input type="email" name="email" th:value="${user.email}" required><button type="submit">修改</button></form>
</body>
</html>
启动服务器测试:
5.实现修改功能
将user_edit.html页面中的form表单修改一下,添加action,添加method,隐藏域的方式提交请求方式put,隐藏域的方式提交id:
<form th:action="@{/user}" method="post"><!--隐藏域的方式设置请求方式为put请求--><input type="hidden" name="_method" value="put"><!--隐藏域的方式提交id--><input type="hidden" name="id" th:value="${user.id}"><label>用户名:</label><input type="text" name="name" th:value="${user.name}" required><label>性别:</label><select name="gender" required><option value="">-- 请选择 --</option><option value="1" th:field="${user.gender}">男</option><option value="0" th:field="${user.gender}">女</option></select><label>邮箱:</label><input type="email" name="email" th:value="${user.email}" required><button type="submit">修改</button>
</form>
编写Controller:
@PutMapping("/user")
public String modify(User user){// 更新数据userDao.update(user);// 重定向return "redirect:/user";
}
编写UserDao:
public void update(User user){for (int i = 0; i < users.size(); i++) {if(user.getId().equals(users.get(i).getId())){users.set(i, user);break;}}
}
启动服务器测试:
6.实现删除功能
删除应该发送DELETE请求,要模拟DELETE请求,就需要使用表单方式提交。因此我们点击删除
超链接时需要采用表单方式提交。 在user_list.html页面添加form表单,并且点击超链接时应该提交表单,代码如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>用户列表</title><link rel="stylesheet" th:href="@{/static/user.css}" type="text/css"></link>
</head>
<body><div class="header"><h1>用户列表</h1></div><div class="add-button-wrapper"><a class="add-button" th:href="@{/toSave}">新增用户</a></div><table><thead><tr><th>编号</th><th>用户名</th><th>性别</th><th>邮箱</th><th>操作</th></tr></thead><tbody><tr th:each="user : ${users}"><td th:text="${user.id}"></td><td th:text="${user.name}"></td><td th:text="${user.gender == 1 ? '男' : '女'}"></td><td th:text="${user.email}"></td><td><a th:href="@{'/user/' + ${user.id}}">修改</a><!--为删除提供一个鼠标单击事件--><a th:href="@{'/user/' + ${user.id}}" onclick="del(event)">删除</a></td></tr></tbody></table><!--为删除操作准备一个form表单,点击删除时提交form表单--><div style="display: none"><form method="post" id="delForm"><input type="hidden" name="_method" value="delete"/></form></div><script>function del(event){// 获取表单let delForm = document.getElementById("delForm");// 设置表单actiondelForm.action = event.target.href;if(window.confirm("您确定要删除吗?")){// 提交表单delForm.submit();}// 阻止超链接默认行为event.preventDefault();}</script>
</body>
</html>
编写Controller:
@DeleteMapping("/user/{id}")
public String del(@PathVariable("id") Long id){// 删除userDao.deleteById(id);// 重定向return "redirect:/user";
}
编写UserDao:
public void deleteById(Long id){for (int i = 0; i < users.size(); i++) {if(id.equals(users.get(i).getId())){users.remove(i);break;}}
}