RESTful
简介
REST(Representational State Transfer):表现层资源状态转移
①资源
资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上可命名的抽象概念。因为资源是一种抽象概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,与面向对象设计类似,资源是以名词为核心来阻止的。一个资源可以由一个或多个URL来标识。URL既是资源的名称,也是资源在web上的地址。
②资源的描述
资源的描述是一段对于资源在某个特定的时刻的转台的描述,可以在客户端-服务器之间转移(交换),资源的表述含可以核对格式。如HTML/XML/JSON/纯文本等。资源的表述格式可以通过协商机制来确定,请求-响应方式的表述通常使用不同的格式。
③状态转移
状态转移说的是:在客户端-服务器之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述来间接实现操作资源的目的。
RESTful实现
具体说,就是http协议里面,四个标识操作方式的动词:GET、POST、PUT、DELETE
它们分别对应四种基本操作:GET用来获取资源、POST用来新建资源、PUT用来更新资源、DELETE用来删除资源。
REST风格提倡URL地址使用同一风格设计,从前到后各个单词是使用斜杠分开,不适用问号键值对方式形式携带请求参数。而不是将发送给服务器的数据作为URL地址的一部分,以保证整体风格的一致性。
操作 | 传统方式 | REST风格 |
---|---|---|
查询操作 | getUserById?id=1 | user/1---->get请求方式 |
新增操作 | saveUser | user----->post请求方式 |
删除操作 | deleteUser?id=1 | user/1---->delete请求 |
更新操作 | updateUser | user—>put请求 |
GET、POST的REST风格
示例
①创建模块完善maven目录结构
②导入依赖
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.1</version>
</dependency>
<dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf-spring5</artifactId><version>3.0.12.RELEASE</version>
</dependency>
③配置web.xml配置文件
<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>/*</url-pattern></filter-mapping><!--注册前端控制器DispatcherServlet--><servlet><servlet-name>springMVCREST</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:SpringMVCRESTFul.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springMVCREST</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
</web-app>
④创建并配置SpringMVCRESTFul.xml
<?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/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd">
<!--开启组件扫描--><context:component-scan base-package="com.louis.controller"></context:component-scan><!--配置视图解析器--><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="test_REST"></mvc:view-controller><mvc:annotation-driven></mvc:annotation-driven>
</beans>
⑤编写页面跳转成功页面success.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org"><title>Title</title>
</head>
<body>
恭喜!成功了!
</body>
</html>
⑥编写test_REST.html文件
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org"><title>Title</title>
</head>
<body>
<a th:href="@{/user}">查询所有用户信息</a><br/>
<a th:href="@{/user/1}">根据id查询用户信息</a><br/>
<form th:action="@{/user}" method="post">用户名:<input type="text" name="username"><br/>密码:<input type="password" name="password"><br/><input type="submit" value="添加"><br/>
</form>
</body>
</html>
⑦设置控制器UserController
@Controller
public class UserController {/*** 使用RESTFul模拟用户资源增删改查* /user GET 查询所有的用户信息* /user/1 GET 根据用户id查询用户信息* /user POST 添加用户信息* /user/1 DELETE 删除用户信息* /user PUT 修改用户信息*/@RequestMapping(value = "/user", method = RequestMethod.GET)public String getAllUser(){System.out.println("查询所有用户信息");return "success";}@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)public String getUserById(){System.out.println("根据用户ID查询用户信息");return "success";}@RequestMapping(value = "/user", method = RequestMethod.POST)public String insertUser(String username, String password){System.out.println("username = " + username + " password = " + password);return "success";}
}
⑧测试
PUT、DELETE的REST风格
需要注意的是,不能直接使用PUT和DELETE操作,在SpringMVC中提供了一个过滤器(HiddenHttpMethodFilter),它是一个隐藏的HTTP请求方式,想要获取PUT和DELETE请求,必须满足两个条件,条件一是请求方式必须为POST,条件二是必须传输一个参数_method,但是它不需要用户传入,是一个隐藏的参数。
web.xml配置HiddenHttpMethodFilter
<!--配置HiddenHttpMethodFilter--><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>
注意:在配置文件中有多个过滤器的时候,由<filter-mapping>
的前后顺序决定,所以为了正确性,需要将设置编码的过滤武器放在首位。
PUT示例
test_REST.html
<form th:action="@{/user}" method="post"><!--条件一,请求方式必须为POST--><input type="hidden" name="_method" value="put"><!--条件二,传输请求参数_method,但对于用户没有意义,值大小写均可-->用户名:<input type="text" name="username"><br/>密码:<input type="password" name="password"><br/><input type="submit" value="修改"><br/>
</form>
UserController
@RequestMapping(value = "/user", method = RequestMethod.PUT)
public String updateUser(String username, String password){System.out.println("修改用户信息 = " + username + "," + password);return "success";
}
DELETE请求
和(修改)PUT请求不同的是,删除(DELETE)通常是超链接。所以在实现删除操作的时候的思路是在超链接上绑定点击事件,在点击事件中需要先阻止超链接的跳转,获得当前的某一个表单,将表单的提交方式设置为post,在其中添加一个隐藏域,设置name=“_method”。
RESTful完整案例
场景:在不连接数据库的情况下使用静态数据集合实现员工信息的增删改查。
1、准备工作
环境搭建
①创建模块并完善项目结构
②导入依赖
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.1</version>
</dependency>
<dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf-spring5</artifactId><version>3.0.12.RELEASE</version>
</dependency>
③配置web.xml
<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>/*</url-pattern></filter-mapping><!--配置请求方式put和delete的hiddenHttpMethodFilter--><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><!--配置springMVC的前端控制器DispatcherServlet--><servlet><servlet-name>DispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springMVC.xml</param-value></init-param><!--将前端控制器的时间提前到服务器启动时--><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>DispatcherServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
</web-app>
④创建并配置springMVC.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--开启扫描组件--><context:component-scan base-package="com.louis"></context:component-scan><!--配值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>
</beans>
⑤创建实体类User
package com.louis.bean;/*** @author XRY* @date 2023年07月01日16:33*/
public class User {private Integer id;private String username;private String email;private Integer gender;public User() {}public User(Integer id, String username, String email, Integer gender) {this.id = id;this.username = username;this.email = email;this.gender = gender;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}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;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", email='" + email + '\'' +", gender='" + gender + '\'' +'}';}
}
⑥创建UserDao
public class UserDao {private static Map<Integer, User> users = null;static{users = new HashMap<Integer, User>();users.put(1001, new User(1001, "E-AA", "aa@163.com", 1));users.put(1002, new User(1002, "E-BB", "bb@163.com", 1));users.put(1003, new User(1003, "E-CC", "cc@163.com", 0));users.put(1004, new User(1004, "E-DD", "dd@163.com", 0));users.put(1005, new User(1005, "E-EE", "ee@163.com", 1));}private static Integer initId = 1006;//新增和修改的方法public void save(User user){if(user.getId() == null){user.setId(initId++);}users.put(user.getId(), user);}//查询全部public Collection<User> getAll(){return users.values();}//通过Id查询public User get(Integer id){return users.get(id);}//通过Id删除public void delete(Integer id){users.remove(id);}
}
⑦创建UserController
package com.louis.controller;import com.louis.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;/*** @author XRY* @date 2023年07月03日19:30*/
@Controller
public class UserController {@Autowiredprivate UserDao userDao;
}
2、功能模块
功能清单
功能 | URL地址 | 请求方式 |
---|---|---|
访问首页 | / | GET |
查询全部数据 | /user | GET |
删除 | /user/2 | DELETE |
跳转到添加页面 | /toAdd | GET |
执行保存操作 | /user | POST |
跳转到更新页面 | /user/2 | GET |
执行更新 | /user | PUT |
①访问首页
在templates下创建首页index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org"><title>首页</title>
</head>
<body>
<h3>首页</h3>
<a th:href="@{/user}">查看员工信息</a>
</body>
</html>
在配置文件springMVC.xml中添加
<mvc:view-controller>
标签
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
<mvc:annotation-driven></mvc:annotation-driven>
②查询数据
@Controller
public class UserController {@Autowiredprivate UserDao userDao;@RequestMapping(value = "/user", method = RequestMethod.GET)public String getAllUser(Model model){/*只需要将数据放在一个较小的域中即可*/Collection<User> userList = userDao.getAll();model.addAttribute("userList", userList);return "employee_list";}
}
创建
user_list.html
用来显示数据
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org"><title>Employee Info</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>username</th><th>email</th><th>gender</th><th>options</th></tr>
<!--通过遍历添加数据到表格中--><tr th:each="user : ${userList}"><td th:text="${user.id}"></td><td th:text="${user.username}"></td><td th:text="${user.email}"></td><td th:text="${user.gender}"></td><td><a href="">delete</a><a href="">update</a></td></tr>
</table>
</body>
</html>
③删除数据
不能通过超链接发送请求,需要通过超链接控制表单。
获取需要删除的id,使用vue将超链接和表单连接,需要引入vue。
user_list.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org"><title>Employee Info</title>
</head>
<body>
<table id="dataTable" border="1" cellspacing="0" cellpadding="0" style="text-align: center"><tr><!--设置表头--><th colspan="5">Employee Info</th></tr><tr><th>id</th><th>username</th><th>email</th><th>gender</th><th>options</th></tr>
<!--通过遍历添加数据到表格中--><tr th:each="user : ${userList}"><td th:text="${user.id}"></td><td th:text="${user.username}"></td><td th:text="${user.email}"></td><td th:text="${user.gender}"></td><td><!--需要根据id删除数据,但是 <a th:href="@{/user/${user.id}}">delete</a>的写法不能够被解析--><!--能够被正常解析的写法th:href="@{'/user/'+${user.id}}"或th:href="@{/user/}+${user.id}}"--><a @click="deleteUser" th:href="@{'/user/'+${user.id}}">delete</a><a href="">update</a></td></tr>
</table>
<!--添加控制删除的表单-->
<form id="deleteForm" method="post"><input type="hidden" value="delete" name="_method">
</form>
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript">var vue = new Vue({/*需要绑定容器,包裹需要操作的标签*/el:"#dataTable",/*需要在超链接上绑定点击事件*/methods:{deleteUser(event){/*获取当前form表单,添加deleteForm*/let deleteForm = document.getElementById("deleteForm");/*设置form表单的提交路径与超链接地址一致*/deleteForm.action = event.target.href;/*提交表单*/deleteForm.submit();/*取消标签的默认行为,阻止超链接直接跳转*/event.preventDefault();}}})
</script>
</body>
</html>
创建删除的控制器
@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
public String deleteUser(@PathVariable("id") Integer id){userDao.delete(id);/*删除之后与原来的表单就没有数据连接了*/return "redirect:/user";
}
需要注意资源未引入的情况,需要重新进行打包。
重新打包
以上是解决服务器中没有vue资源文件,同时vue需要被默认的servlet处理,在springMVC中添加开放对静资源访问标签
<!--开放对静态资源的访问,需要<mvc:annotation-driven>标签-->
<mvc:default-servlet-handler/>
测试
④添加功能
在表头部分的action表格中加入一个添加的超链接。
user_list.html
<tr><th>id</th><th>username</th><th>email</th><th>gender</th><th>options(<a th:href="@{/toAdd}">add</a>)</th>
</tr>
由于添加操作不需要处理其他的业务逻辑,所以可以在springMVC.xml中使用view-controller标签去实现控制器功能。
<mvc:view-controller path="/toAdd" view-name="user_add"></mvc:view-controller>
创建user_add.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org"><title>add user</title>
</head>
<body>
<form th:action="@{/user}" method="post">username:<input type="text" name="username"><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">
</form>
</body>
</html>
控制器
@RequestMapping(value = "/user", method = RequestMethod.POST)
public String addUser(User user){userDao.save(user);return "redirect:/user";
}
测试
⑤更新操作
更新操作和删除操作类似,但是需要表单显示相关内容并提交(回显)。
user_list.html
<a th:href="@{'/user/' + ${user.id}}">update</a>
控制器
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
/*需要通过id查询数据,同时还需要将查询出来的数据在域中实现共享*/
public String getUserById(@PathVariable("id") Integer id, Model model){User user = userDao.get(id);model.addAttribute("user", user);return "user_update";
}
创建user_update.html实现回显
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org"><title>add user</title>
</head>
<body>
<!--修改使用PUT方式,需要保证表单提交方式为post-->
<form th:action="@{/user}" method="post"><input type="hidden" name="_method" value="put"><input type="hidden" name="id" th:value="${user.id}">username:<input type="text" name="username" th:value="${user.username}"><br/>email:<input type="text" name="email" th:value="${user.email}"><br/>gender:<input type="radio" name="gender" value="1" th:field="${user.gender}">male<input type="radio" name="gender" value="0" th:field="${user.gender}">female<br/><input type="submit" value="update">
</form>
</body>
</html>
创建修改的控制器
@RequestMapping(value = "/user", method = RequestMethod.PUT)
public String updateUser(User user){userDao.save(user);return "redirect:/user";
}
测试