前言
关于 REST 概念的阐述,是 Roy Fielding 大神在他的 博士论文 中提出的,有兴趣的小伙伴可以去看一下,Restful web service是指遵守了 REST 风格的web服务, 可以看下阮一峰老师的 RESTful API 最佳实践。但要记住的是 REST 是一种设计风格,它背后的理念是使用 HTTP 动词 GET,POST, PUT, DELETE 来对应服务的 CURD 操作,并且使用 JSON 来请求数据和接收数据。
在设计符合 REST 理念的服务接口时,可以参考以下指导方针:
- 使用 HTTP 动词(GET, POST, PUT, DELETE)围绕服务展开操作
- 使用 URI 来传达意图
- 请求和响应使用 JSON
- 使用 HTTP 状态码来传达结果
下面使用一个对员工进行增删改查的例子来实践 RESTful 设计。
创建项目
项目结构图如下:
![]()
pom 依赖文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> </parent> <groupId>top.yekongle</groupId> <artifactId>springboot-restful-sample</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot-restful-sample</name> <description>RESTful project for Spring Boot</description>
<properties> <java.version>1.8</java.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency>
<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</project>
|
代码编写
Employee.java, 员工实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package com.yekongle.rest.entity;
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id;
import lombok.Data;
@Data @Entity public class Employee { @Id @GeneratedValue private Long id;
private String name; private String role;
public Employee() { }
public Employee(String name, String role) { this.name = name; this.role = role; } }
|
EmployeeNotFoundException.java, 自定义 Exception,找不到员工时抛出该 Exception
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package top.yekongle.restful.exception;
public class EmployeeNotFoundException extends RuntimeException { private static final long serialVersionUID = 1L;
public EmployeeNotFoundException(Long id) { super("Could not find employee " + id); } }
|
EmployeeRepository.java, 员工数据操作接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package top.yekongle.restful.repository;
import org.springframework.data.jpa.repository.JpaRepository; import top.yekongle.restful.entity.Employee;
public interface EmployeeRepository extends JpaRepository<Employee, Long>{
}
|
GlobalExceptonConfig.java, 全局 Exception 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package top.yekongle.restful.config;
import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus;
import top.yekongle.restful.exception.EmployeeNotFoundException;
@ControllerAdvice public class GlobalExceptonConfig {
@ResponseBody @ExceptionHandler(EmployeeNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public String employeeNotFoundHandler(EmployeeNotFoundException ex) { return ex.getMessage(); } @ResponseBody @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public String systemErrorHandler(Exception ex) { return ex.getMessage(); } }
|
DatabaseConfig.java, 数据库配置,插入两条初始化数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package top.yekongle.restful.config;
import org.springframework.boot.CommandLineRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
import lombok.extern.slf4j.Slf4j; import top.yekongle.restful.entity.Employee; import top.yekongle.restful.repository.EmployeeRepository;
@Configuration @Slf4j public class DatabaseConfig {
@Bean public CommandLineRunner initDatabase(EmployeeRepository repository) { return args -> { log.info("Preloading " + repository.save(new Employee("张三", "初级程序员"))); log.info("Preloading " + repository.save(new Employee("李四", "高级程序员"))); }; } }
|
EmployeeController.java,表现层,处理请求 API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| package top.yekongle.restful.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import top.yekongle.restful.entity.Employee; import top.yekongle.restful.exception.EmployeeNotFoundException; import top.yekongle.restful.repository.EmployeeRepository;
@RestController public class EmployeeController { @Autowired EmployeeRepository repository;
@GetMapping("/employees") public ResponseEntity<?> all() { List<Employee> employeeList = repository.findAll(); return ResponseEntity.ok(employeeList); }
@PostMapping("/employees") public ResponseEntity<?> newEmployee(@RequestBody Employee newEmployee) { repository.save(newEmployee); return ResponseEntity.status(HttpStatus.CREATED).body(newEmployee); }
@GetMapping("/employees/{id}") public ResponseEntity<?> one(@PathVariable Long id) { Employee employee = repository.findById(id).orElseThrow(() -> new EmployeeNotFoundException(id)); return ResponseEntity.ok(employee); }
@PutMapping("/employees/{id}") public ResponseEntity<?> replaceEmployee(@RequestBody Employee newEmployee, @PathVariable Long id) { repository.findById(id) .map(employee -> { employee.setName(newEmployee.getName()); employee.setRole(newEmployee.getRole()); return repository.save(employee); }) .orElseGet(() -> { newEmployee.setId(id); return repository.save(newEmployee); }); return ResponseEntity.noContent().build(); }
@DeleteMapping("/employees/{id}") public ResponseEntity<?> deleteEmployee(@PathVariable Long id) { repository.deleteById(id); return ResponseEntity.noContent().build(); } }
|
运行测试
启动项目,查看控制台日志可看到两条初始化数据插入Database
![]()
使用 Postman 对接口进行测试
查询所有员工:http://localhost:8080/employees
![]()
查询某个员工:http://localhost:8080/employees/1
![]()
查询某个不存在的员工:http://localhost:8080/employees/3
![]()
将 id 为 1 的员工名字改为赵六:http://localhost:8080/employees/1
![]()
新增员工:http://localhost:8080/employees
![]()
再查看所有员工,可见新增了王五的记录
![]()
删除员工:http://localhost:8080/employees/3
![]()
项目已上传至 Github: https://github.com/yekongle/springboot-code-samples/tree/master/springboot-restful-sample , 希望对小伙伴们有帮助哦。