SpringBoot RESTful API 设计规范与最佳实践
·
RESTful API 是后端接口的主流设计风格。好的 API 设计让前端用得舒服、后端维护省心、接口文档清晰。这篇总结在 SpringBoot 中实现 RESTful API 的规范和最佳实践。
一、RESTful 核心原则
资源(Resource)而不是动作(Action)
❌ /getUser?id=1 ← 动词 + 参数
❌ /deleteUser?id=1 ← 动词
❌ /user/add ← 动词
✅ GET /users/{id} ← 查询用户
✅ DELETE /users/{id} ← 删除用户
✅ POST /users ← 新增用户
✅ PUT /users/{id} ← 修改用户
HTTP 动词与 CRUD 对应:
| 方法 | 操作 | 示例 | 说明 |
|---|---|---|---|
| GET | 查询 | GET /users | 获取用户列表 |
| GET | 查询 | GET /users/1 | 获取单个用户 |
| POST | 新增 | POST /users | 新增用户 |
| PUT | 全量修改 | PUT /users/1 | 替换用户信息 |
| PATCH | 部分修改 | PATCH /users/1 | 修改用户部分字段 |
| DELETE | 删除 | DELETE /users/1 | 删除用户 |
二、URL 设计规范
1. 命名规范
// 名词复数形式,不要动词
/api/users // ✅
/api/getUserList // ❌
// 层级用 / 表示
/users/{uid}/orders // 用户的订单
/users/{uid}/orders/{oid} // 用户的某个订单
// 查询参数用于过滤、排序、分页
GET /users?page=1&size=20&sort=createTime,desc
2. Controller 示例
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping
public ResultVO<Page<User>> list(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(required = false) String keyword) {
// GET /api/users?page=1&size=20&keyword=张
return ResultVO.success(userService.page(page, size, keyword));
}
@GetMapping("/{id}")
public ResultVO<User> get(@PathVariable Long id) {
// GET /api/users/1
return ResultVO.success(userService.getById(id));
}
@PostMapping
public ResultVO<User> create(@RequestBody @Valid User user) {
// POST /api/users
userService.save(user);
return ResultVO.success(user);
}
@PutMapping("/{id}")
public ResultVO<User> update(@PathVariable Long id, @RequestBody @Valid User user) {
// PUT /api/users/1
user.setId(id);
userService.updateById(user);
return ResultVO.success(user);
}
@DeleteMapping("/{id}")
public ResultVO<?> delete(@PathVariable Long id) {
// DELETE /api/users/1
userService.removeById(id);
return ResultVO.success("删除成功");
}
}
三、统一响应格式
所有接口返回统一的 JSON 结构,前端不用处理多种格式。
@Data
@Accessors(chain = true)
public class ResultVO<T> {
private int code;
private String message;
private T data;
private long timestamp;
public ResultVO() {
this.timestamp = System.currentTimeMillis();
}
public static <T> ResultVO<T> success(T data) {
return new ResultVO<T>()
.setCode(200)
.setMessage("操作成功")
.setData(data);
}
public static <T> ResultVO<T> error(int code, String message) {
return new ResultVO<T>()
.setCode(code)
.setMessage(message);
}
}
统一响应示例:
// 成功
{
"code": 200,
"message": "操作成功",
"data": { "id": 1, "name": "张三" },
"timestamp": 1712345678000
}
// 失败
{
"code": 400,
"message": "参数错误:用户名为空",
"data": null,
"timestamp": 1712345678000
}
四、参数校验
1. 常用注解
@Data
public class UserCreateDTO {
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度2-20")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 32, message = "密码长度6-32")
private String password;
@Email(message = "邮箱格式不正确")
private String email;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
@Min(value = 0, message = "年龄不能小于0")
@Max(value = 150, message = "年龄不能大于150")
private Integer age;
}
2. 分组校验
// 不同操作使用不同校验规则
public interface CreateGroup {}
public interface UpdateGroup {}
@Data
public class UserDTO {
@Null(groups = CreateGroup.class, message = "新增时ID必须为空")
@NotNull(groups = UpdateGroup.class, message = "修改时ID不能为空")
private Long id;
@NotBlank(message = "用户名不能为空")
private String name;
}
// Controller
@PostMapping
public ResultVO<?> create(@RequestBody @Validated(CreateGroup.class) UserDTO dto) {
return ResultVO.success(userService.create(dto));
}
@PutMapping("/{id}")
public ResultVO<?> update(@RequestBody @Validated(UpdateGroup.class) UserDTO dto) {
return ResultVO.success(userService.update(dto));
}
3. 全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
// 参数校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResultVO<?> handleValidation(MethodArgumentNotValidException e) {
String msg = e.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(";"));
return ResultVO.error(400, msg);
}
// 业务异常
@ExceptionHandler(BusinessException.class)
public ResultVO<?> handleBusiness(BusinessException e) {
return ResultVO.error(e.getCode(), e.getMessage());
}
// 其他异常
@ExceptionHandler(Exception.class)
public ResultVO<?> handleOther(Exception e) {
log.error("系统异常", e);
return ResultVO.error(500, "服务器内部错误");
}
}
五、分页接口规范
@GetMapping
public ResultVO<PageResult<UserVO>> list(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
Page<User> pageResult = userService.page(new Page<>(page, size));
PageResult<UserVO> result = new PageResult<>();
result.setPage(page);
result.setSize(size);
result.setTotal(pageResult.getTotal());
result.setPages(pageResult.getPages());
result.setList(userMapper.toVOList(pageResult.getRecords()));
return ResultVO.success(result);
}
{
"code": 200,
"data": {
"page": 1,
"size": 20,
"total": 156,
"pages": 8,
"list": [...]
}
}
六、API 版本管理
// 方案一:URL 路径版本
@RestController
@RequestMapping("/api/v1/users")
public class UserControllerV1 { }
@RestController
@RequestMapping("/api/v2/users")
public class UserControllerV2 { }
// 方案二:自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiVersion {
int value() default 1;
}
七、Swagger 文档
@RestController
@RequestMapping("/api/users")
@Tag(name = "用户管理", description = "用户增删改查接口")
public class UserController {
@GetMapping("/{id}")
@Operation(summary = "查询用户", description = "根据ID获取用户详细信息")
public ResultVO<User> get(@Parameter(description = "用户ID") @PathVariable Long id) {
return ResultVO.success(userService.getById(id));
}
@PostMapping
@Operation(summary = "新增用户")
public ResultVO<User> create(@RequestBody @Valid @Validated(CreateGroup.class)
UserCreateDTO dto) {
return ResultVO.success(userService.create(dto));
}
}
八、接口安全建议
// 1. 敏感信息不返回
@JsonIgnore
private String password;
// 2. 接口限流
@RateLimit(key = "user:list", max = 10, window = 1)
// 3. 权限控制
@PreAuthorize("hasRole('ADMIN')")
// 4. 输入过滤
@Pattern(regexp = "^[a-zA-Z0-9_]+$")
九、完整接口示例
@RestController
@RequestMapping("/api/v1/products")
@Tag(name = "商品管理")
public class ProductController {
@GetMapping
@Operation(summary = "分页查询商品")
public ResultVO<PageResult<ProductVO>> list(ProductQueryDTO query) {
return ResultVO.success(productService.queryPage(query));
}
@GetMapping("/{id}")
@Operation(summary = "获取商品详情")
public ResultVO<ProductVO> get(@PathVariable Long id) {
return ResultVO.success(productService.getDetail(id));
}
@PostMapping
@Operation(summary = "新增商品")
public ResultVO<ProductVO> create(@RequestBody @Valid ProductCreateDTO dto) {
return ResultVO.success(productService.create(dto));
}
@PutMapping("/{id}")
@Operation(summary = "修改商品")
public ResultVO<ProductVO> update(
@PathVariable Long id, @RequestBody @Valid ProductUpdateDTO dto) {
dto.setId(id);
return ResultVO.success(productService.update(dto));
}
@DeleteMapping("/{id}")
@Operation(summary = "删除商品")
public ResultVO<?> delete(@PathVariable Long id) {
productService.delete(id);
return ResultVO.success("删除成功");
}
@PatchMapping("/{id}/stock")
@Operation(summary = "修改库存")
public ResultVO<?> updateStock(
@PathVariable Long id, @RequestParam int stock) {
productService.updateStock(id, stock);
return ResultVO.success("修改成功");
}
}
总结
RESTful API 设计记住三句话:
URL 用名词复数,不用动词
HTTP 方法对应 CRUD 操作
返回统一的 JSON 结构
API 设计是后端的基本功,好的 API 设计让前后端协作事半功倍。
💡 觉得有用的话,点赞 + 关注【张老师技术栈】吧!每周更新 Java/Python/爬虫 实战干货,不让你白来。
更多推荐


所有评论(0)