一、REST风格简介
REST(Representational State Transfer),表现形式状态转换,它是一种软件架构风格
传统风格资源描述形式
- http://localhost/user/getById?id=1 查询id为1的用户信息
- http://localhost/user/saveUser 保存用户信息
REST风格描述形式
- http://localhost/user/1
- http://localhost/user
REST的优点
- 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
- 书写简化
按照REST风格访问资源时使用行为动作区分对资源进行了何种操作
- http://localhost/users 查询全部用户信息 GET(查询)
- http://localhost/users/1 查询指定用户信息 GET(查询)
- http://localhost/users 添加用户信息 POST(新增/保存)
- http://localhost/users 修改用户信息 PUT(修改/更新)
- http://localhost/users/1 删除用户信息 DELETE(删除)
上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范
根据REST风格对资源进行访问称为RESTful
- 使用客户/服务器(b/s、 c/s)模型。客户和服务器之间通过一个统一的接口来互相通讯。
- 层次化的系统。在一个REST系统中,客户端并不会固定地与一个服务器打交道。
- 无状态。在一个REST系统中,服务端并不会保存有关客户的任何状态。也就是说,客户端自身负责用户状态的维持,并在每次发送请求时都需要提供足够的信息。
- 可缓存。REST系统需要能够恰当地缓存请求,以尽量减少服务端和客户端之间的信息传输,以提高性能。
- 统一的接口。一个REST系统需要使用一个统一的接口来完成子系统之间以及服务与用户之间的交互。这使得REST系统中的各个子系统可以独自完成演化。
如果一个系统满足了上面所列出的五条约束,那么该系统就被称为是RESTful的。
二、RESTFUL入门
1.环境准备
创建一个web的Maven项目,在pom.xml中添加相关依赖
<?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>SpringMVC_07</groupId>
<artifactId>SpringMVC_07</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>SpringMVC_07 Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
创建对应的配置类
ServletContainersInitConfig
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMVCConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
//乱码处理
protected Filter[] getServletFilters(){
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
SpringMVCConfig
@Configuration
@ComponentScan("com.controller")
//开启json数据类型自动转换
@EnableWebMvc
public class SpringMVCConfig {
}
编写模型类User
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
编写UserController控制类
@Controller
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String save(User user) {
System.out.println("user save ..." + user);
return "{'module':'user save'}";
}
@RequestMapping("/delete")
@ResponseBody
public String delete(Integer id) {
System.out.println("user delete ..." + id);
return "{'module':'user delete'}";
}
@RequestMapping("/update")
@ResponseBody
public String update(User user) {
System.out.println("uesr update ..." + user);
return "{'module':'user update'}";
}
@RequestMapping("/getById")
@ResponseBody
public String getById(Integer id) {
System.out.println("user getById ..." + id);
return "{'module':'user getById'}";
}
@RequestMapping("/findAll")
@ResponseBody
public String getAll() {
System.out.println("user getAll ...");
return "{'module':'user getAll'}";
}
}
最终结构如下
删除web.xml,配置Tomcat环境,具体操作可见此处
2.新增
在UserController中修改save方法
//设置当前请求方法为POST,表示REST风格中的添加操作
@RequestMapping(value = "/users", method = RequestMethod.POST)
@ResponseBody
public String save(User user) {
System.out.println("user save ..." + user);
return "{'module':'user save'}";
}
将请求路径更改为/users
在apifox中发送post请求
控制台输出
3.删除
在UserController中修改delete方法
//设置当前请求方法为DELETE,表示REST风格中的删除操作
@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id) {
System.out.println("user delete ..." + id);
return "{'module':'user delete'}";
}
当方法形参的名称和路径{}中的值不一致时,可以使用@PathVariable注解解决
//设置当前请求方法为DELETE,表示REST风格中的删除操作
@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable("id") Integer userid) {
System.out.println("user delete ..." + userid);
return "{'module':'user delete'}";
}
@PathVariable:用于接收路径参数,使用{参数名称}描述路径参数
将请求路径更改为/users/1
在apifox中发送delete请求
控制台输出
4.修改
在UserController中修改update方法
//设置当前请求方法为PUT,表示REST风格中的修改操作
@RequestMapping(value = "/users", method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody User user) {
System.out.println("uesr update ..." + user);
return "{'module':'user update'}";
}
将请求路径更改为/users
在apifox中发送put请求
控制台输出
5.按ID查询
在UserController中修改saveById方法
//设置当前请求方法为GET,表示REST风格中的查询操作
@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
@ResponseBody
public String getById(@PathVariable Integer id) {
System.out.println("user getById ..." + id);
return "{'module':'user getById'}";
}
将请求路径更改为/users/1
在apifox中发送get请求
控制台输出
6.查询所有
在UserController中修改saveAll方法
//设置当前请求方法为GET,表示REST风格中的查询操作
@RequestMapping(value = "/users", method = RequestMethod.GET)
@ResponseBody
public String getAll() {
System.out.println("user getAll ...");
return "{'module':'user getAll'}";
}
将请求路径更改为/users
在apifox中发送get请求
控制台输出
对于解@RequestBody、@RequestParam、@PathVariable三个形参注解的区别
- @RequestParam:用于接收url地址传参或表单传参
- @RequestBody:用于接收json数据
- @PathVariable:用于接收路径参数,使用{参数名称描述路径}
- 后期开发中,发送请求参数超过一个时,以json格式为主,@RequestBody应用较广
- 如果发送非json格式数据,选用@RequestParam接收请求参数
- 采用RESTful进行开发,当参数数量较少时,例如一个,可以采用@PathVariable接收请求路径变量,通常用于传递id值
三、RESTFUL快速开发
在RESTFUL入门中,我们可以发现每个方法都定义了访问路径/users,每个@RequestMapping注解都要使用method属性定义请求方法,每个方法响应json都需要加上@ResponseBody注解,这些操作都有很高的重复性,于是,SpringMVC为我们提供了如下解决方案
//@Controller
@RestController
@RequestMapping("/users")
public class UserController {
//设置当前请求方法为POST,表示REST风格中的添加操作
//@RequestMapping(value = "/users", method = RequestMethod.POST)
//@ResponseBody
@PostMapping
public String save(User user) {
System.out.println("user save ..." + user);
return "{'module':'user save'}";
}
//设置当前请求方法为DELETE,表示REST风格中的删除操作
//@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)
//@ResponseBody
@DeleteMapping("/{id}")
public String delete(@PathVariable("id") Integer userid) {
System.out.println("user delete ..." + userid);
return "{'module':'user delete'}";
}
//设置当前请求方法为PUT,表示REST风格中的修改操作
//@RequestMapping(value = "/users", method = RequestMethod.PUT)
//@ResponseBody
@PutMapping
public String update(@RequestBody User user) {
System.out.println("uesr update ..." + user);
return "{'module':'user update'}";
}
//设置当前请求方法为GET,表示REST风格中的查询操作
//@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
//@ResponseBody
@GetMapping("/{id}")
public String getById(@PathVariable Integer id) {
System.out.println("user getById ..." + id);
return "{'module':'user getById'}";
}
//设置当前请求方法为GET,表示REST风格中的查询操作
//@RequestMapping(value = "/users", method = RequestMethod.GET)
//@ResponseBody
@GetMapping
public String getAll() {
System.out.println("user getAll ...");
return "{'module':'user getAll'}";
}
}
@RestController:设置当前控制器类为RESTful风格,等同于@Conrtroller与@ResponseBody两个注解组合功能
- 将每个方法的@ResponseBody提到类上面,让所有的方法都有@ResponseBody的功能,而@RestController注解替换了@Controller与@ResponseBody注解,简化书写
- 将@RequesMapping提到类上面,用来定义所有方法共同的访问路径
- 使用@GetMapping、@PostMapping、@PutMapping、@DeleteMapping代替@RequestMapping中的method属性定义请求
四、SpringMVC拦截静态资源处理
当我们项目中有静态页面时
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>测试静态页面拦截</h1>
</body>
</html>
此时我们运行服务器,访问http://localhost/pages/test.html
控制台报错
这是因为SpringMVC拦截了静态资源,根据/pages/test.html去controller找对应的方法,找不到所以会报404的错误
SpringMVC拦截静态资源的原因是因为在ServletContainersInitConfig中配置的是/,http://localhost/pages/test.html满足这个规则
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
我们可以通过配置SpringMVCSupport将静态资源放行
@Configuration
public class SpringMVCSupport extends WebMvcConfigurationSupport {
//设置静态资源访问过滤,当前类需要设置为配置类,并被扫描加载
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//当访问/pages/???的时候,从/pages目录下查找内容
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
}
}
具体项目是css,js等静态资源都需配置放行
在SpringMVCConfig中添加可以扫描到SpringMVCSupport的包
@Configuration
@ComponentScan({"com.controller","com.config"})
//开启json数据类型自动转换
@EnableWebMvc
public class SpringMVCConfig {
}
此时重启服务器,访问http://localhost/pages/test.htm