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/爬虫 实战干货,不让你白来。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐