SpringBoot之实现RESTful接口

REST,即Representational State Transfer的缩写,对这个词组的翻译是表现层状态转化

RESTful是一种软件设计风格,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。

SpringMVC对RESTful风格的接口有着天然的支持,本篇将讲述如何在SpringBoot中怎样写。

1.重要注解

在讲述使用之前,想要理解SpringMVC的几个常用注解:

  1. @Controller:修饰class,用来创建处理http请求的对象
  2. @RestController:Spring4之后加入的注解,原来在@Controller中返回json需要@ResponseBody来配合,如果直接用@RestController替代@Controller就不需要再配置@ResponseBody,默认返回json格式。
  3. @RequestMapping:配置url映射
  4. @PostMapping: 这个是@RequestMapping+POST方法的简写
  5. @RequestHeader: 请求Header参数
  6. @PathVariable: URL路径参数,比如/user/{id}中的id参数
  7. @RequestParam: URL请求参数,比如/user?id=1中的id参数
  8. @RequestBody: 请求Body参数

下面我们尝试使用Spring MVC来实现一组对User对象操作的RESTful API,配合注释详细说明在Spring MVC中如何映射HTTP请求、如何传参、如何编写单元测试。

2.API设计

请求类型 URL 功能说明
GET /uer/ 查询全部
POST /uer 插入数据
GET /uer/{id} 根据id查询
PUT /uer/{id} 根据id更新
DELETE /uer/{id} 根据id删除
POST /user/{id} 测试乐观锁
GET /user/pageInfo/{pageNo} 分页查询

RESTful架构有一些典型的设计误区,就是URI包含动词。因为”资源”表示一种实体,所以应该是名词,URI不应该有动词,动词应该放在HTTP协议中。 上面设计的API的URI中都是名词。

3.定义实体

这里我们使用MybatisPlus的代码生成器,简化操作,只需编写RESTful风格的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
/**
* <p>
*
* </p>
*
* @author 路飞
* @since 2021-01-05
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value = "Users", description = "Users")
public class Users implements Serializable {

private static final long serialVersionUID = 1L;

@ApiModelProperty(value = "主键ID")
@TableId(value = "id", type = IdType.AUTO)
private Long id;

@ApiModelProperty(value = "姓名")
private String name;

@ApiModelProperty(value = "年龄")
private Integer age;

@ApiModelProperty(value = "邮箱")
private String email;

@Version
private Integer version;

@ApiModelProperty(value = "逻辑删除")
@TableLogic
private Integer deleted;

@ApiModelProperty(value = "创建时间")
private Date create_time;

@ApiModelProperty(value = "更新时间")
private Date update_time;
}

4.controller的编写

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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/**
* <p>
* 前端控制器
* </p>
*
* @author 路飞
* @since 2021-01-05
*/
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

@Autowired
private UserService userService;

@Autowired
private UserMapper userMapper;

/**
* 查询全部
*
* @return
*/
@ApiOperation("查询全部")
@GetMapping("/")
public BaseResponse<List<Users>> getAll() {
List<Users> list = userService.list(null);
return new BaseResponse<>(true, "查询成功", list);

}

/**
* 根据id查
*
* @param id
* @return
*/
@ApiOperation("根据id查询")
@GetMapping("/{id}")
public BaseResponse<Users> getById(@PathVariable Long id) {
Users user = userService.getById(id);
return new BaseResponse<>(true, "查询成功", user);

}

/**
* 根据id删除
*
* @param id
* @return
*/
@ApiOperation("根据id删除")
@DeleteMapping("/{id}")
public BaseResponse<String> dellteUser(@PathVariable Long id) {
userService.removeById(id);
return new BaseResponse<>(true, "删除成功", "200");
}


/**
* 根据id更新
*
* @param id
* @return
*/
@ApiOperation("根据id更新")
@PutMapping("/{id}")
public BaseResponse<String> updateUser(@PathVariable Long id, @ModelAttribute Users user) {
userService.updateById(user);
log.info("更新成功");
return new BaseResponse<>(true, "更新成功", "200");

}

/**
* 插入数据
*
* @param user
* @return
*/
@ApiOperation("插入数据")
@PostMapping("/")
public BaseResponse<String> insUser(@ModelAttribute Users user) {
userService.save(user);

return new BaseResponse<>(true, "插入成功", "200");

}

/**
* 测试乐观锁
*
* @param id
* @param user
* @return
*/
@ApiOperation("测试乐观锁")
@PostMapping("/{id}")
public BaseResponse<Users> insUser(@PathVariable Long id, @ModelAttribute Users user) {
//先拿数据,再更新,乐观锁才起作用
Users users = userService.getById(id);
userService.updateById(user);
Users userServiceById = userService.getById(id);
return new BaseResponse<>(true, "成功", userServiceById);
}

/**
* 分页显示
*
* @param pageNo
* @return
*/
@ApiOperation("分页查询")
@GetMapping("/pageInfo/{pageNo}")
public BaseResponse<List<Users>> pageInfoUser(@PathVariable int pageNo) {
//默认每页5个
// 参数一:当前页
// 参数二:页面大小
Page<Users> page = new Page<>(pageNo, 5);
userService.page(page, null);
List<Users> list = page.getRecords();
return new BaseResponse<>(true, "查询成功", list);
}
}

5.测试

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
@SpringBootTest
class SpringbootRestfulApplicationTests {

@Autowired
private UserMapper userMapper;

// 测试插入
@Test
public void testInsert() {
Users user = new Users();
user.setName("Alen");
user.setAge(3);
user.setEmail("1234@qq.com");

int result = userMapper.insert(user); // 帮我们自动生成id
System.out.println(result); // 受影响的行数
System.out.println(user); // 发现,id会自动回填
}

//测试乐观锁
@Test
public void testversion() {
Users user = userMapper.selectById(1L);
//修改
user.setName("最强的");
user.setAge(11);
//更新
userMapper.updateById(user);
}

// 测试乐观锁失败!多线程下
@Test
public void testOptimisticLocker2() {
// 线程 1
Users user = userMapper.selectById(1L);
user.setName("Luffy11111");
user.setEmail("134@qq.com");

// 模拟另外一个线程执行了插队操作
Users user2 = userMapper.selectById(1L);
user2.setName("Luffy22222");
user2.setEmail("134@qq.com");
userMapper.updateById(user2);

// 自旋锁来多次尝试提交!
userMapper.updateById(user); // 如果没有乐观锁就会覆盖插队线程的值!
}
}

使用Postman测试Post,Delete,Put类型的接口,测试成功!

6.GitHub源码

springboot-restful