1、什么是 RESTFul?
2、主要是对网络资源进行操作,那么什么是资源?
3、什么是资源的表述?【就是资源的体现形式】
4、什么是状态转移?
在HTTP协议里面,有四个表示基本操作的动词:
REST 风格提倡使用地址传值的方式
不从功能角度,从思想角度我们分析一下应该如何实现?【针对某个资源而言】
同一个请求路径,不同的请求方式来表示不同的操作
如果我们需要传递参数, 我们在控制器方法上的RequestMapping注解对应位置,使用 /{参数名}的方式
@GetMapping("/user/{id}")
public String getUserById(){
System.out.println("根据id查询用户信息");
return "success";
}
对应请求的超链接我们可以这样写,使用了占位符,就必须传递对应位置参数的数据
<a th:href="@{/user/1}">根据id查询用户信息</a><br>
如果我们需要通过前端表单获取数据,然后添加我们的用户信息【我们采用POST方式】
@PostMapping("/user")
public String insertUser(String username, String password){
System.out.println("添加用户信息: " + username + " " + password);
return "success";
}
在前端页面表单method的属性需要指定为 POST【测试POST有两种办法,表单、AJAX】
<form th:action="@{/user}" method="post">
用户名 <input type="text" name="username"><br>
密码 <input type="text" name="password"><br>
<input type="submit" value="添加">
</form>
对于浏览器大多只支持 POST 和 GET 请求,其他请求默认为 GET 请求,那么我们想实现PUT 和 DELETE 请求应该怎么办呢?
☀️ 在SpringMVC 中提供了HiddenHttpMethodFilter 帮我们将POST请求转换为 DELETE 或 PUT 请求
我们如何使用这个机制?
需要在 web.xml 中注册这个过滤器,因为过滤器是按注册位置先后执行的,所以我们要把设置编码的过滤器放到最前面
<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>/*
然后通过表单发起请求,并将请求方式设置为 POST,再添加一个name属性参数值为 _method = “请求方式”【PUT / DELETE】
<form th:action="@{/user}" method="put">
<input type="hidden" name="_method" value="PUT">
用户名 <input type="text" name="username"><br>
密码 <input type="text" name="password"><br>
<input type="submit" value="修改">
</form>
在控制器中设置对应的方法就行
@PutMapping("/user")
public String updateUser(String username, String password){
System.out.println("修改用户信息: " + username + " " + password);
return "success";
}
这里使用了控制器方法的形参获取请求参数,如果对获取请求参数不太了解可以参考 传送门
接下来我们通过一个CRUD案例,来演示具体功能的实现
需求:实现对员工信息的增删改查 【我们写一个集合来存储数据,并不真正的去连接数据库】
准备工作:
创建新模块 springMVC-rest,添加打包方式和使用的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu</groupId>
<artifactId>springMVC-reset</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
编写我们的 pojo 类,封装员工信息 Employee
package com.atguigu.rest.bean;
/**
* @author Bonbons
* @version 1.0
*/
public class Employee {
private Integer id;
private String lastName;
private String email;
// 1代表男,0代表女
private Integer gender;
public Employee() {
}
public Employee(Integer id, String lastName, String email, Integer gender) {
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", gender=" + gender +
'}';
}
}
在dao层创建一个类完成对数据库的操作,在这个类中用一个集合模拟数据库 EmployeeDao
package com.atguigu.rest.dao;
import com.atguigu.rest.bean.Employee;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* 我们用Dao模拟操作数据库,实际不去连接数据库
* @author Bonbons
* @version 1.0
*/
@Repository
public class EmployeeDao {
private static Map<Integer, Employee> employees = null;
static{
employees = new HashMap<Integer, Employee>();
employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1));
employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1));
employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0));
employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0));
employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1));
}
// 记录下一个用户可获得的 ID
private static Integer initId = 1006;
/**
* 添加和修改用户信息
* @param employee 一个员工对象
*/
public void save(Employee employee){
if(employee.getId() == null){
employee.setId(initId++);
}
employees.put(employee.getId(), employee);
}
/**
*
* @return
*/
public Collection<Employee> getAll(){
return employees.values();
}
public Employee get(Integer id){
return employees.get(id);
}
public void delete(Integer id){
employees.remove(id);
}
}
在 web.xml 中注册过滤器和前端控制器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--配置编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*
HiddenHttpMethodFilter
org.springframework.web.filter.HiddenHttpMethodFilter
HiddenHttpMethodFilter
/*
DispatcherServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:springMVC.xml
1
DispatcherServlet
/
编写我们springMVC的核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描组件-->
<context:component-scan base-package="com.atguigu.rest.controller,
com.atguigu.rest.dao"/>
<!--配置Thymeleaf视图解析器-->
<bean id="ViewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1" />
<property name="characterEncoding" value="UTF-8" />
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/" />
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>
<!--配置访问首页-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
<mvc:view-controller path="/toAdd" view-name="employee_add"></mvc:view-controller>
<!--开启mvc注解配置-->
<mvc:annotation-driven />
<!--开放对静态资源的访问-->
<mvc:default-servlet-handler />
</beans>
开始对我们提供的功能展开分析:

接下来逐步实现我们的功能:
☀️ 首页
我们在视图控制器处已经完成了首页自动定位的工作,在首页里面通过一条超链接查询全部数据 index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<a th:href="@{/employee}">查询所有员工信息</a>
</body>
</html>
☀️ 查询所有员工信息
编写我们查询结果显示页面 employee_list
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>EmployInfo</title>
</head>
<body>
<table border="1" cellspacing="0" cellpadding="0" style="text-align: center">
<tr>
<th colspan="5">Employee Info</th>
</tr>
<tr>
<th>id</th>
<th>lastName</th>
<th>email</th>
<th>gender</th>
<th>options(<a th:href="@{/toAdd}">add</a> )</th>
</tr>
<tr th:each="employee : ${employeeList}">
<td th:text="${employee.id}"></td>
<td th:text="${employee.lastName}"></td>
<td th:text="${employee.email}"></td>
<td th:text="${employee.gender}"></td>
<td>
<!--当把id放到@{}内部+就不会报错了, 删除功能的超链接-->
<a class="deleteA" @click="deleteEmployee" th:href="@{'/employee/'+${employee.id}}">delete</a>
<a th:href="@{'/employee/'+${employee.id}}">update</a>
</td>
</tr>
</table>
</body>
</html>
编写我们的控制器方法,后续只给出方法 EmployeeController
package com.atguigu.rest.controller;
import com.atguigu.rest.bean.Employee;
import com.atguigu.rest.dao.EmployeeDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
/**
* @author Bonbons
* @version 1.0
*/
@Controller
public class EmployeeController {
@Autowired
private EmployeeDao employeeDao;
@RequestMapping(value = "/employee", method = RequestMethod.GET)
public String getAllEmploy(Model model){
Collection<Employee> employeeList = employeeDao.getAll();
// 通过Model的对象将我们查询的所有员工信息添加到请求域中
model.addAttribute("employeeList", employeeList);
return "employee_list";
}
}

☀️ 保存新员工信息
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加员工信息</title>
</head>
<body>
<form th:action="@{/employee}" method="post">
lastName <input type="text" name="lastName"><br>
email <input type="text" name="email"><br>
gender <input type="radio" name="gender" value="1"> male
<input type="radio" name="gender" value="0"> female <br>
<input type="submit" value="add"><br>
</form>
</body>
</html>
<th>options(<a th:href="@{/toAdd}">add</a> )</th>
// 添加员工信息
@RequestMapping(value = "/employee", method = RequestMethod.POST)
public String addEmployeeAdd(Employee employee){
employeeDao.save(employee);
// 为了显示添加效果,在添加成功后直接跳转到我们的查询全部员工的页面
return "redirect:/employee";
}

☀️ 修改员工信息
完善在 表格中的 update 超链接
<a th:href="@{'/employee/'+${employee.id}}">update</a>
在控制器中完成根据 id 查询信息并保存到 request 请求域中,跳转到我们的更新页面
// 根据id查询,然后回显数据到更新表单
@RequestMapping(value = "/employee/{id}", method = RequestMethod.GET)
public String getEmployeeById(@PathVariable("id") Integer id, Model model){
// 根据传递进来的参数进行查询
Employee employee = employeeDao.get(id);
// 将查询到的数据添加到我们的请求域中
model.addAttribute("employee", employee);
return "employee_update";
}
编写我们更新数据的页面 employee_update.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>更新员工信息</title>
</head>
<body>
<form th:action="@{/employee}" method="post">
<!--这里需要完成数据回显,就是显示出每项原来的数据是什么-->
<!--尽管我们添加对象的时候不用传入id,但是应该设置隐藏属性,到时候传递回去封装数据用-->
<!--回显后我们需要修改数据,所以将请求方式设置为 put,在点击提交后再次发起请求-->
<input type="hidden" name="_method" value="put" />
<input type="hidden" name="id" th:value="${employee.id}">
lastName: <input type="text" name="lastName" th:value="${employee.lastName}"><br>
email: <input type="text" name="email" th:value="${employee.email}"><br>
gender: <input type="radio" name="gender" value="1" th:field="${employee.gender}">male
<input type="radio" name="gender" value="0" th:field="${employee.gender}">female<br>
<input type="submit" value="update"><br>
</form>
</body>
</html>
编写我们实际更新员工信息的控制器方法
// 更新我们的员工信息
@RequestMapping(value = "/employee", method = RequestMethod.PUT)
public String updateEmployee(Employee employee){
employeeDao.save(employee);
return "redirect:/employee";
}

☀️ 删除员工信息
删除只需要在对应员工信息后面点击一下,所以通过超链接发起请求【@click=“deleteEmployee” 通过vue处理点击事件】
<a @click="deleteEmployee" th:href="@{'/employee/'+${employee.id}}">delete</a>
因为删除是 DELETE 请求,所以我们通过一个表单转换请求
<!-- 通过超链接控制表单的提交,将post请求转换为delete请求 -->
<form id="delete_form" method="post">
<!-- 我们使用了HiddenHttpMethodFilter过滤器,传递_method请求参数,value为最终的请求方式 -->
<input type="hidden" name="_method" value="delete"/>
</form>
需要引入我们的 vue.js ,点击可以跳转下载vue.js
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
引入资源之后,需要重新打包导入我们的资源,然后需要在核心配置文件中配置开放静态资源的访问
<!--开启mvc注解配置-->
<mvc:annotation-driven />
<!--开放对静态资源的访问-->
<mvc:default-servlet-handler />
这俩都要配置,如果只有第二个配置(handler),就会导致只能访问静态资源
接下来通过vue完成绑定【注意是一个单独的标签,不嵌套到表单内】
script type="text/javascript">
var vue = new Vue({
el:"#dataTable",
methods:{
//event表示当前事件
deleteEmployee:function (event) {
//通过id获取表单标签
var delete_form = document.getElementById("deleteEmployee");
//将触发事件的超链接的href属性为表单的action属性赋值
delete_form.action = event.target.href;
//提交表单
delete_form.submit();
//阻止超链接的默认跳转行为
event.preventDefault();
}
}
});
</script>
在控制器中编写控制器方法
// 根据id删除员工信息
@RequestMapping (value = "/employee/{id}", method = RequestMethod.DELETE)
public String deleteEmployee(@PathVariable("id") Integer id){
employeeDao.delete(id);
return "redirect:/employee";
}

📖 补充知识点: