服务器之家

服务器之家 > 正文

浅谈Spring Boot 开发REST接口最佳实践

时间:2021-03-18 12:21     来源/作者:固安李庆海

本文介绍了Spring Boot 开发REST接口最佳实践,分享给大家,具体如下:

HTTP动词与SQL命令对应

GET

?
1
2
3
4
5
从服务器获取资源,可一个或者多个,对应SQL命令中的SELECT
GET /users
获取服务器上的所有的用户信息
GET /users/ID
获取指定ID的用户信息

POST

?
1
2
3
在服务器上创建一个新资源,对应SQL命令中的CREATE
POST /users
创建一个新的用户

PUT

?
1
2
3
在服务器上更新一个资源,客户端提供改变后的完整资源,对应SQL命令中的UPDATE
PUT /users/ID
更新指定ID的用户的全部信息

DELETE

?
1
2
3
从服务器上删除一个资源,对应SQL命令中的DELETE
DELETE /users/ID
删除指定ID的用户信息

PATCH

?
1
2
3
在服务器更新一个资源的部分属性,对应SQL命令中的UPDATE
PATCH /users/ID
更新指定ID的用户的某个属性

URL中的约定

URL中名词使用复数形式

URL中的名称是使用单数还是使用复数的问题,争议由来已久。URL中的名词一般对应数据库中的表,表中存储的是同类数据, 在实践中我是强制使用复数形式 ,看上去更舒服些。

?
1
2
3
4
/users
/users/1
/roles
/roles/1

至于一些不规则的、不可数的名词就见仁见智吧。

?
1
2
3
4
5
6
7
8
/heroes
/heroes/1
/people
/people/1
/foots
/foots/1
/feet
/feet/1

版本

讲版本号加入到URL中以应对不兼容的和破坏性的更改。发布新API时,客户端可以自如的迁移到新API,不会因调用完全不同的新API而陷入窘境。使用直观的“V”前缀来表示后面的数字是版本号,不需要次级版本号,不应该频繁的发布API版本。

?
1
2
/edu/v1/users
/edu/v1/roles

对可选的、复杂的参数使用查询字符串

为了让URL更小、更简洁,为资源设置一个基本URL,讲可选的、复杂的参数用查询字符串表示。

?
1
/edu/v1/users?enabled=1&roleid=1

提供分页信息

一次性返回数据库中的所有的资源不是一个好主意,因此需要提供分页机制。通常使用数据库中众所周知的参数offset和limit

?
1
/edu/v1/users?enabled=1&offset=1&limit=15

如果客户端没有传递这些参数,则应使用默认值,通常offset=0,limit=10。

非资源请求使用动词

有时API调用并不涉及资源,在这种情况下,服务器执行一个操作病将结果返回给客户端。

?
1
/edu/v1/calc?p=100

考虑特定资源和跨资源搜索

提供对特定止缘的搜索很容易,只需要使用相应的资源集合,并将搜索字符串附加到查询参数中即可。

?
1
/edu/v1/users?username=李庆海

如果需要对所有资源提供全局搜索,则需要使用其他方法。

?
1
/edu/v1/search?key=李庆海

响应结果

使用小驼峰命名法作为属性标识符

通常,RESTful Web服务将被JavaScript编写的客户端使用。客户端会将JSON响应转换为JavaScript对象,然后调用其属性。因此,最好遵循JavaScript代码通用规范。

?
1
2
3
person.year_of_birth // 不推荐,违反JavaScript代码通用规范
person.YearOfBirth // 不推荐,JavaScript构造方法命名
person.yearOfBirth // 推荐

提供分页信息

返回结果比较多时,应提供分页信息。

?
1
2
3
4
5
6
7
8
{
 "page": 0,
 "size": 10,
 "total": 3465,
 "obj": [
  
 ]
}

Spring MVC开发REST接口

常用注解

@RestController

@RestController是@ResponseBody和@Controller的组合注解。

@RequestMapping

此注解即可以作用在控制器的某个方法上,也可以作用在此控制器类上。当控制器在类级别上添加@RequestMapping注解时,这个注解会应用到控制器的所有处理器方法上。处理器方法上的@RequestMapping注解会对类级别上的@RequestMapping的声明进行补充。

@PostMapping

组合注解,是@RequestMapping(method =RequestMethod.POST)的缩写。

@PutMapping

组合注解,是@RequestMapping(method = RequestMethod.PUT)的缩写。

@PatchMapping

组合注解,是@RequestMapping(method = RequestMethod.PATCH)的缩写。

@DeleteMapping

组合注解,是@RequestMapping(method = RequestMethod.DELETE)的缩写。

@GetMapping

组合注解,是@RequestMapping(method = RequestMethod.GET)的缩写。

@PathVariable

获取url中的数据。

@RequestParam

获取请求参数的值。

REST接口及Swagger 编写API文档示例

关于Swagger的使用可参考Spring Boot 项目中使用Swagger2 。方法体中的代码不重要,重要的是方法的签名以及与HTTP动词的映射。

?
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import java.util.Date;
import javax.persistence.EntityNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import cn.com.infcn.jianshu.Service.UserService;
import cn.com.infcn.jianshu.exception.BizException;
import cn.com.infcn.jianshu.exception.LoginNameOrPasswordErrorException;
import cn.com.infcn.jianshu.exception.ResourceExistsException;
import cn.com.infcn.jianshu.model.User;
import cn.com.infcn.jianshu.util.JsonResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
 
/**
 * 系统用户Controller
 *
 * @author 李庆海
 *
 */
@Api(value = "系统用户接口", tags = "系统管理")
@RestController
@RequestMapping("/v3/edu/users")
public class UserController {
 
 @Autowired
 private UserService userService;
 
 /**
  * 添加用户,注册
  *
  * @param loginName
  *   登录账号
  * @param userName
  *   用户名称
  * @param password
  *   登录密码
  * @param roleId
  *   用户角色
  * @return
  * @throws ResourceExistsException
  */
 @ApiOperation(value = "添加用户")
 @PostMapping("/")
 public JsonResult create(
   @ApiParam(name = "loginName", value = "登录账号", required = true) @RequestParam(required = true) @RequestBody String loginName,
   @ApiParam(name = "userName", value = "用户名称", required = true) @RequestParam(required = true) @RequestBody String userName,
   @ApiParam(name = "password", value = "登录密码", required = true) @RequestParam(required = true) @RequestBody String password,
   @ApiParam(name = "roleId", value = "用户角色编号", required = true) @RequestParam(required = true) @RequestBody String roleId)
   throws ResourceExistsException {
  boolean exists = this.userService.exists(loginName);
  if (exists) {
   throw new ResourceExistsException(loginName);
  }
  User user = userService.create(loginName, password, userName, roleId);
  return JsonResult.success(user);
 }
 
 /**
  * 用户凭借登录账号和登录密码进行登录
  *
  * @param loginName
  *   登录账号
  * @param password
  *   登录密码
  * @throws EntityNotFoundException
  */
 @ApiOperation(value = "根据用户编号查询用户信息")
 @GetMapping("/login")
 public JsonResult login(
   @ApiParam(name = "loginName", value = "登录账号", required = true) @RequestParam(required = true) String loginName,
   @ApiParam(name = "password", value = "登录密码", required = true) @RequestParam(required = true) String password)
   throws LoginNameOrPasswordErrorException {
  User user = this.userService.login(loginName, password);
  if (null == user) {
   throw new LoginNameOrPasswordErrorException();
  }
  return JsonResult.success(user);
 }
 
 /**
  * 根据用户编号查询用户信息
  *
  * @param id
  *   用户编号
  * @throws EntityNotFoundException
  */
 @ApiOperation(value = "根据用户编号查询用户信息")
 @GetMapping("/{id}")
 public JsonResult read(
   @ApiParam(name = "id", value = "用户编号,主键", required = true) @PathVariable(required = true) String id)
   throws EntityNotFoundException {
  User user = this.userService.getOne(id);
  return JsonResult.success(user);
 }
 
 /**
  * 账户注销,不删除用户的数据
  *
  * @param userId
  *   用户编号
  * @return
  */
 @ApiOperation(value = "注销账户")
 @PatchMapping("/{id}")
 public JsonResult cancel(
   @ApiParam(name = "id", value = "用户编号,主键", required = true) @PathVariable(required = true) String id)
   throws EntityNotFoundException {
  this.userService.cancel(id);
  return JsonResult.success();
 }
 
 /**
  * 重置密码
  *
  * @param id
  *   用户编号
  * @param password
  *   新登录密码
  * @return
  */
 @ApiOperation(value = "重置密码")
 @PatchMapping("/")
 public JsonResult updatePassword(
   @ApiParam(name = "id", value = "用户编号,主键", required = true) @RequestParam(required = true) String id,
   @ApiParam(name = "password", value = "新登录密码", required = true) @RequestParam(required = true) String password) {
  this.userService.updatePassword(id, password);
  return JsonResult.success();
 }
 
 /**
  * 多条件组合查询
  *
  * @param userName
  *   用户名称
  * @param roleId
  *   用户角色
  * @param start
  *   开始日期
  * @param end
  *   结束日期
  * @param page
  *   分页,从0开始
  * @param size
  *   每页的行数,默认10
  * @return
  * @throws BizException
  */
 @ApiOperation(value = "用户信息查询")
 @GetMapping("/")
 public JsonResult query(
   @ApiParam(name = "userName", value = "用户名称,查询关键词", required = false) @RequestParam(required = false) String userName,
   @ApiParam(name = "roleId", value = "用户角色编号", required = false) @RequestParam(required = false) String roleId,
   @ApiParam(name = "start", value = "用户角色编号", required = false) @RequestParam(required = false) Date start,
   @ApiParam(name = "end", value = "用户角色编号", required = false) @RequestParam(required = false) Date end,
   @ApiParam(name = "page", value = "分页,第几页,从1开始", defaultValue = "1", required = true) @RequestParam(defaultValue = "1", required = true) int page,
   @ApiParam(name = "size", value = "每页的行数,正整数", defaultValue = "10", required = true) @RequestParam(defaultValue = "10", required = true) int size)
   throws BizException {
  Page<User> datas = this.userService.findDatas(userName, roleId, start, end, page, size);
  if (null == datas || null == datas.getContent() || datas.getContent().isEmpty()) {
   throw new BizException("用户不存在");
  }
  return JsonResult.success(datas);
 }
 
}

Swagger2接口文档效果图

浅谈Spring Boot 开发REST接口最佳实践

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.jianshu.com/p/1dbb71f78104

标签:

相关文章

热门资讯

2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
Intellij idea2020永久破解,亲测可用!!!
Intellij idea2020永久破解,亲测可用!!! 2020-07-29
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总 2020-11-13
返回顶部