UserController类讲解
用户管理控制器,实现了用户CRUD操作的RESTful API:
1. 类结构与核心注解
1.1 控制器声明
@RestController
@RequestMapping("/api/users")
public class UserController
@RestController 深度解析:
- 组合注解:
@Controller
+@ResponseBody
- 自动序列化:返回的对象自动转换为JSON
- Spring MVC集成:无需手动配置JSON转换器
- RESTful设计:专为API接口设计
@RequestMapping(“/api/users”) 路径设计:
- 统一前缀:所有用户相关接口以
/api/users
开头 - 版本控制:便于后续添加
/api/v2/users
- 语义清晰:一看就知道是用户管理接口
1.2 依赖注入与日志
private static final Logger log = LoggerFactory.getLogger(UserController.class);@Autowired
private UserService userService;
日志配置:
- SLF4J框架:统一的日志接口
- 类级别Logger:每个类有独立的日志记录器
- 静态final:性能优化,避免重复创建
依赖注入优势:
- 松耦合:Controller不直接依赖具体实现
- 易测试:可以注入Mock对象进行单元测试
- 配置驱动:Spring容器管理对象生命周期
2. RESTful API设计解析
2.1 HTTP方法与URL映射
功能 | HTTP方法 | URL | 说明 |
---|---|---|---|
创建用户 | POST | /api/users | 创建资源 |
删除用户 | DELETE | /api/users/{id} | 删除指定资源 |
更新用户 | PUT | /api/users/{id} | 完整更新资源 |
查询单个用户 | GET | /api/users/{id} | 获取指定资源 |
查询所有用户 | GET | /api/users | 获取资源列表 |
分页查询 | GET | /api/users/page | 分页获取资源 |
RESTful设计优点:
- 语义明确:HTTP方法表达操作意图
- URL简洁:资源导向的路径设计
- 标准化:遵循HTTP协议规范
3. 核心接口详细解析
3.1 创建用户接口
@PostMapping
public Result<User> createUser(@RequestBody User user) {try {User createdUser = userService.createUser(user);return Result.success(createdUser);} catch (Exception e) {log.error("创建用户失败", e);return Result.<User>error(e.getMessage());}
}
@PostMapping 解析:
- HTTP POST:用于创建资源
- 无路径参数:直接映射到类路径
/api/users
@RequestBody 详解:
- JSON反序列化:自动将请求体JSON转换为User对象
- Content-Type要求:前端需设置
application/json
- 参数验证:可结合Bean Validation进行参数校验
前端请求示例:
// 前端请求
fetch('/api/users', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({username: 'newuser',password: '123456',email: 'newuser@example.com'})
})
3.2 删除用户接口
@DeleteMapping("/{id}")
public Result<Void> deleteUser(@PathVariable Long id) {try {boolean success = userService.deleteUser(id);if (success) {return Result.success();} else {return Result.<Void>error("用户不存在");}} catch (Exception e) {log.error("删除用户失败", e);return Result.<Void>error(e.getMessage());}
}
@PathVariable 详解:
- 路径参数提取:从URL
/api/users/123
中提取id=123
- 类型转换:自动将字符串转换为Long类型
- 参数验证:Spring自动处理类型转换异常
业务逻辑处理:
- 存在性检查:先检查用户是否存在
- 状态反馈:根据删除结果返回不同信息
- 幂等性:多次删除同一资源结果一致
3.3 更新用户接口
@PutMapping("/{id}")
public Result<User> updateUser(@PathVariable Long id, @RequestBody User user) {try {user.setId(id); // 关键:设置ID确保更新正确的用户User updatedUser = userService.updateUser(user);return Result.success(updatedUser);} catch (Exception e) {log.error("更新用户失败", e);return Result.<User>error(e.getMessage());}
}
设计要点:
- PUT语义:完整替换资源
- ID一致性:URL中的ID覆盖请求体中的ID
- 返回更新后数据:便于前端同步最新状态
前端使用示例:
// 更新用户信息
fetch('/api/users/123', {method: 'PUT',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({username: 'updateduser',email: 'updated@example.com'})
})
3.4 查询接口设计
单用户查询
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable Long id)
用户名查询
@GetMapping("/username/{username}")
public Result<User> getUserByUsername(@PathVariable String username)
邮箱查询
@GetMapping("/email/{email}")
public Result<User> getUserByEmail(@PathVariable String email)
URL设计模式:
- RESTful风格:
/资源/查询条件/值
- 语义明确:URL即文档
- 易于理解:符合直觉的路径结构
3.5 分页查询核心实现
@GetMapping("/page")
public Result<Map<String, Object>> getUsersByPage(@RequestParam(defaultValue = "1") Integer pageNum,@RequestParam(defaultValue = "10") Integer pageSize,@RequestParam(required = false) String username) {
@RequestParam 详解:
- defaultValue:提供默认值,提升用户体验
- required = false:可选参数,支持条件查询
- 类型转换:自动将字符串转换为Integer
分页逻辑:
// 条件分支:搜索 vs 普通分页
if (username != null && !username.trim().isEmpty()) {users = userService.searchUsersByUsername(username, pageNum, pageSize);total = userService.getSearchTotalCount(username);
} else {users = userService.getUsersByPage(pageNum, pageSize);total = userService.getTotalCount();
}
返回数据结构:
Map<String, Object> data = new HashMap<>();
data.put("list", users); // 当前页数据
data.put("total", total); // 总记录数
data.put("pageNum", pageNum); // 当前页码
data.put("pageSize", pageSize); // 每页大小
data.put("pages", (total + pageSize - 1) / pageSize); // 总页数
前端调用示例:
// 普通分页
fetch('/api/users/page?pageNum=1&pageSize=10')// 搜索分页
fetch('/api/users/page?pageNum=1&pageSize=10&username=zhang')
3.6 密码修改接口
@PutMapping("/{id}/password")
public Result<Void> changePassword(@PathVariable Long id, @RequestBody Map<String, String> passwordData)
设计特点:
- 专用接口:密码修改独立于普通更新
- 安全考虑:需要提供原密码验证
- Map参数:灵活接收键值对数据
请求体格式:
{"oldPassword": "123456","newPassword": "newpass123"
}
安全验证流程:
// 1. 参数验证
if (oldPassword == null || newPassword == null) {return Result.<Void>error("密码不能为空");
}// 2. 用户存在性检查
User user = userService.getUserById(id);
if (user == null) {return Result.<Void>error("用户不存在");
}// 3. 原密码验证
if (!oldPassword.equals(user.getPassword())) {return Result.<Void>error("原密码错误");
}
4. 异常处理机制
4.1 统一异常处理模式
try {// 业务逻辑
} catch (Exception e) {log.error("操作失败", e);return Result.<Type>error(e.getMessage());
}
异常处理优势:
- 用户友好:返回可读的错误信息
- 系统稳定:防止异常导致系统崩溃
- 便于调试:记录详细的错误日志
4.2 改进
// 更细粒度的异常处理
try {// 业务逻辑
} catch (BusinessException e) {// 业务异常log.warn("业务异常: {}", e.getMessage());return Result.error(e.getMessage());
} catch (DataAccessException e) {// 数据访问异常log.error("数据库操作失败", e);return Result.error("系统繁忙,请稍后重试");
} catch (Exception e) {// 未知异常log.error("系统异常", e);return Result.error("系统错误");
}
5. 响应格式统一
5.1 成功响应
{"code": 200,"message": "操作成功","data": {"id": 1,"username": "admin","email": "admin@example.com"}
}
5.2 失败响应
{"code": 500,"message": "用户不存在","data": null
}
5.3 分页响应
{"code": 200,"message": "操作成功","data": {"list": [...],"total": 50,"pageNum": 1,"pageSize": 10,"pages": 5}
}
-
RESTful标准:符合HTTP协议语义
-
统一响应格式:便于前端处理
-
完整CRUD操作:功能齐全
-
异常处理机制:提升系统稳定性
-
日志记录:便于问题排查
-
参数验证:基础的输入校验
-
参数验证增强:使用Bean Validation
-
权限控制:添加认证授权
-
接口文档:使用Swagger注解
-
批量操作:支持批量删除/更新
-
缓存策略:添加查询缓存
-
限流控制:防止接口被恶意调用
7.1 参数验证增强
@PostMapping
public Result<User> createUser(@RequestBody @Valid User user, BindingResult bindingResult) {if (bindingResult.hasErrors()) {return Result.error("参数验证失败:" + bindingResult.getFieldError().getDefaultMessage());}// 业务逻辑...
}
7.2 权限控制
@GetMapping("/{id}")
@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
public Result<User> getUserById(@PathVariable Long id) {// 业务逻辑...
}
7.3 API文档
@GetMapping("/{id}")
@ApiOperation(value = "根据ID查询用户", notes = "返回用户详细信息")
@ApiParam(name = "id", value = "用户ID", required = true)
public Result<User> getUserById(@PathVariable Long id) {// 业务逻辑...
}