当前位置: 首页 > news >正文

SpringBoot3.x入门到精通系列:2.4 RESTful API设计

SpringBoot 3.x RESTful API设计

🎯 RESTful API设计原则

REST (Representational State Transfer) 是一种架构风格,用于设计网络应用程序的API。

核心原则

  1. 资源导向 - 一切皆资源,每个资源都有唯一的URI
  2. 统一接口 - 使用标准的HTTP方法
  3. 无状态 - 每个请求都包含处理该请求所需的所有信息
  4. 可缓存 - 响应应该明确标识是否可缓存
  5. 分层系统 - 客户端无需知道是否直接连接到最终服务器

🌐 HTTP方法与CRUD操作

HTTP方法CRUD操作描述示例
GETRead获取资源GET /api/users
POSTCreate创建资源POST /api/users
PUTUpdate完整更新资源PUT /api/users/1
PATCHUpdate部分更新资源PATCH /api/users/1
DELETEDelete删除资源DELETE /api/users/1

📋 URL设计规范

1. 资源命名规范

// ✅ 好的设计
GET    /api/users              // 获取用户列表
GET    /api/users/123          // 获取特定用户
POST   /api/users              // 创建用户
PUT    /api/users/123          // 更新用户
DELETE /api/users/123          // 删除用户// 嵌套资源
GET    /api/users/123/orders   // 获取用户的订单
POST   /api/users/123/orders   // 为用户创建订单// ❌ 不好的设计
GET    /api/getUsers            // 动词形式
POST   /api/createUser          // 动词形式
GET    /api/user_list           // 下划线

2. 实际API设计示例

@RestController
@RequestMapping("/api/v1")
@CrossOrigin(origins = "*")
public class UserApiController {private final UserService userService;public UserApiController(UserService userService) {this.userService = userService;}/*** 获取用户列表* GET /api/v1/users?page=0&size=10&sort=name,asc*/@GetMapping("/users")public ResponseEntity<PagedResponse<UserDTO>> getUsers(@RequestParam(defaultValue = "0") int page,@RequestParam(defaultValue = "10") int size,@RequestParam(defaultValue = "id") String sort,@RequestParam(required = false) String search) {PagedResponse<UserDTO> users = userService.findAll(page, size, sort, search);return ResponseEntity.ok(users);}/*** 根据ID获取用户* GET /api/v1/users/{id}*/@GetMapping("/users/{id}")public ResponseEntity<UserDTO> getUserById(@PathVariable Long id) {UserDTO user = userService.findById(id);return ResponseEntity.ok(user);}/*** 创建用户* POST /api/v1/users*/@PostMapping("/users")public ResponseEntity<UserDTO> createUser(@RequestBody @Valid CreateUserRequest request,HttpServletRequest httpRequest) {UserDTO user = userService.create(request);// 返回201状态码和Location头URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(user.getId()).toUri();return ResponseEntity.created(location).body(user);}/*** 完整更新用户* PUT /api/v1/users/{id}*/@PutMapping("/users/{id}")public ResponseEntity<UserDTO> updateUser(@PathVariable Long id,@RequestBody @Valid UpdateUserRequest request) {UserDTO user = userService.update(id, request);return ResponseEntity.ok(user);}/*** 部分更新用户* PATCH /api/v1/users/{id}*/@PatchMapping("/users/{id}")public ResponseEntity<UserDTO> patchUser(@PathVariable Long id,@RequestBody Map<String, Object> updates) {UserDTO user = userService.patch(id, updates);return ResponseEntity.ok(user);}/*** 删除用户* DELETE /api/v1/users/{id}*/@DeleteMapping("/users/{id}")public ResponseEntity<Void> deleteUser(@PathVariable Long id) {userService.delete(id);return ResponseEntity.noContent().build();}/*** 批量删除用户* DELETE /api/v1/users?ids=1,2,3*/@DeleteMapping("/users")public ResponseEntity<BatchDeleteResponse> batchDeleteUsers(@RequestParam List<Long> ids) {BatchDeleteResponse response = userService.batchDelete(ids);return ResponseEntity.ok(response);}
}

📊 统一响应格式

1. 成功响应格式

public class ApiResponse<T> {private boolean success;private String message;private T data;private long timestamp;public static <T> ApiResponse<T> success(T data) {return new ApiResponse<>(true, "操作成功", data, System.currentTimeMillis());}public static <T> ApiResponse<T> success(String message, T data) {return new ApiResponse<>(true, message, data, System.currentTimeMillis());}// 构造函数、getter和setterpublic ApiResponse(boolean success, String message, T data, long timestamp) {this.success = success;this.message = message;this.data = data;this.timestamp = timestamp;}// getter和setter方法public boolean isSuccess() { return success; }public void setSuccess(boolean success) { this.success = success; }public String getMessage() { return message; }public void setMessage(String message) { this.message = message; }public T getData() { return data; }public void setData(T data) { this.data = data; }public long getTimestamp() { return timestamp; }public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
}

2. 分页响应格式

public class PagedResponse<T> {private List<T> content;private int page;private int size;private long totalElements;private int totalPages;private boolean first;private boolean last;public PagedResponse(List<T> content, int page, int size, long totalElements) {this.content = content;this.page = page;this.size = size;this.totalElements = totalElements;this.totalPages = (int) Math.ceil((double) totalElements / size);this.first = page == 0;this.last = page >= totalPages - 1;}// getter和setter方法public List<T> getContent() { return content; }public void setContent(List<T> content) { this.content = content; }public int getPage() { return page; }public void setPage(int page) { this.page = page; }public int getSize() { return size; }public void setSize(int size) { this.size = size; }public long getTotalElements() { return totalElements; }public void setTotalElements(long totalElements) { this.totalElements = totalElements; }public int getTotalPages() { return totalPages; }public void setTotalPages(int totalPages) { this.totalPages = totalPages; }public boolean isFirst() { return first; }public void setFirst(boolean first) { this.first = first; }public boolean isLast() { return last; }public void setLast(boolean last) { this.last = last; }
}

3. 错误响应格式

public class ErrorResponse {private boolean success = false;private String error;private String message;private int status;private String path;private long timestamp;private List<FieldError> fieldErrors;public ErrorResponse(String error, String message, int status, String path) {this.error = error;this.message = message;this.status = status;this.path = path;this.timestamp = System.currentTimeMillis();}public static class FieldError {private String field;private Object rejectedValue;private String message;public FieldError(String field, Object rejectedValue, String message) {this.field = field;this.rejectedValue = rejectedValue;this.message = message;}// getter和setter方法public String getField() { return field; }public void setField(String field) { this.field = field; }public Object getRejectedValue() { return rejectedValue; }public void setRejectedValue(Object rejectedValue) { this.rejectedValue = rejectedValue; }public String getMessage() { return message; }public void setMessage(String message) { this.message = message; }}// getter和setter方法public boolean isSuccess() { return success; }public void setSuccess(boolean success) { this.success = success; }public String getError() { return error; }public void setError(String error) { this.error = error; }public String getMessage() { return message; }public void setMessage(String message) { this.message = message; }public int getStatus() { return status; }public void setStatus(int status) { this.status = status; }public String getPath() { return path; }public void setPath(String path) { this.path = path; }public long getTimestamp() { return timestamp; }public void setTimestamp(long timestamp) { this.timestamp = timestamp; }public List<FieldError> getFieldErrors() { return fieldErrors; }public void setFieldErrors(List<FieldError> fieldErrors) { this.fieldErrors = fieldErrors; }
}

🔧 HTTP状态码使用

1. 常用状态码

状态码含义使用场景
200OK成功获取资源
201Created成功创建资源
204No Content成功删除资源
400Bad Request请求参数错误
401Unauthorized未认证
403Forbidden无权限
404Not Found资源不存在
409Conflict资源冲突
422Unprocessable Entity验证失败
500Internal Server Error服务器错误

2. 状态码使用示例

@RestController
@RequestMapping("/api/v1/users")
public class UserController {@GetMapping("/{id}")public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {try {UserDTO user = userService.findById(id);return ResponseEntity.ok(user);  // 200} catch (UserNotFoundException e) {return ResponseEntity.notFound().build();  // 404}}@PostMappingpublic ResponseEntity<UserDTO> createUser(@RequestBody @Valid CreateUserRequest request) {try {UserDTO user = userService.create(request);URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(user.getId()).toUri();return ResponseEntity.created(location).body(user);  // 201} catch (EmailAlreadyExistsException e) {return ResponseEntity.status(HttpStatus.CONFLICT).build();  // 409}}@DeleteMapping("/{id}")public ResponseEntity<Void> deleteUser(@PathVariable Long id) {try {userService.delete(id);return ResponseEntity.noContent().build();  // 204} catch (UserNotFoundException e) {return ResponseEntity.notFound().build();  // 404}}
}

🔍 API版本控制

1. URL路径版本控制

@RestController
@RequestMapping("/api/v1/users")
public class UserV1Controller {// v1版本的用户API
}@RestController
@RequestMapping("/api/v2/users")
public class UserV2Controller {// v2版本的用户API
}

2. 请求头版本控制

@RestController
@RequestMapping("/api/users")
public class UserController {@GetMapping(headers = "API-Version=1")public ResponseEntity<UserV1DTO> getUserV1(@PathVariable Long id) {// v1版本逻辑}@GetMapping(headers = "API-Version=2")public ResponseEntity<UserV2DTO> getUserV2(@PathVariable Long id) {// v2版本逻辑}
}

📝 API文档

1. 使用OpenAPI 3.0 (Swagger)

<dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.2.0</version>
</dependency>

2. API文档注解

@RestController
@RequestMapping("/api/v1/users")
@Tag(name = "用户管理", description = "用户相关的API接口")
public class UserController {@Operation(summary = "获取用户列表", description = "分页获取用户列表")@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "成功获取用户列表"),@ApiResponse(responseCode = "400", description = "请求参数错误")})@GetMappingpublic ResponseEntity<PagedResponse<UserDTO>> getUsers(@Parameter(description = "页码,从0开始") @RequestParam(defaultValue = "0") int page,@Parameter(description = "每页大小") @RequestParam(defaultValue = "10") int size) {PagedResponse<UserDTO> users = userService.findAll(page, size);return ResponseEntity.ok(users);}@Operation(summary = "创建用户", description = "创建新的用户")@PostMappingpublic ResponseEntity<UserDTO> createUser(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "用户创建请求", required = true)@RequestBody @Valid CreateUserRequest request) {UserDTO user = userService.create(request);return ResponseEntity.status(201).body(user);}
}

🔗 下一篇

在下一篇文章中,我们将学习SpringBoot的数据访问与JPA,了解如何进行数据库操作。


本文关键词: RESTful API, HTTP方法, 状态码, API设计, 版本控制, OpenAPI

http://www.dtcms.com/a/312687.html

相关文章:

  • 电脑声音标志显示红叉的原因
  • Spring Batch的2种STEP定义方式
  • spring-ai-alibaba 学习(二十)——graph之检查点
  • VUE2 学习笔记16 插槽、Vuex
  • 大屏项目展示
  • python学智能算法(三十一)|SVM-Slater条件理解
  • 【MySQL进阶】------MySQL程序
  • 全排列二(回溯算法)
  • 位图:用bit改变存储格局
  • Linux 文件与目录操作命令宝典
  • Apache Shenyu 本地启动及快速入门
  • 【Bluetooth】【基础篇】第二章 关于蓝牙协议栈架构与其硬件方案架构大致概述
  • 【JS】JavaScript中的this详解
  • Android 优化 - 日志 Log
  • LeetCode513:找树最左下角的值(bfs+dfs)
  • 【鸿蒙高级】
  • [硬件电路-148]:数字电路 - 什么是CMOS电平、TTL电平?还有哪些其他电平标准?发展历史?
  • 动感按钮:如何打造交互感十足的点击动画效果
  • 【1】WPF界面开发入门—— 图书馆程序:登录界面设计
  • 基于图像识别与分类的中国蛇类识别系统
  • [硬件电路-151]:数字电路 - 模拟电路与数字电路的本质
  • 【数据结构】二叉树的顺序结构实现
  • SQL注入SQLi-LABS 靶场less31-38详细通关攻略
  • 托福阅读38-3
  • 使用AssemblyAI将音频数据转换成文本
  • AI生成图片工具分享!
  • Linux的权限概念
  • 关于Web前端安全之XSS攻击防御增强方法
  • 【视频内容创作】PR的关键帧动画
  • 机器学习第三课之逻辑回归(三)LogisticRegression