ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Spring Boot 4 生产级实战——统一返回结果与全局异常处理,让接口更规范

Spring Boot 4 生产级实战——统一返回结果与全局异常处理,让接口更规范 本篇是《Spring Boot 4 学习从入门到大神》专栏第 8 篇。一个专业的后端项目接口返回格式必须统一异常必须被优雅处理。本文将手把手教你封装统一返回结果、全局异常处理器并给出生产级可直接复制的模板。一、为什么必须统一返回结果❌ 混乱的返回示例// 成功 { id: 1, username: zhangsan } // 失败 { error: 用户不存在 } // 另一个接口失败 { code: 500, msg: 服务器异常 }问题前端无法用统一逻辑解析错误信息格式不一致状态码与业务码混淆✅ 统一返回结构推荐{ code: 200, message: success, data: { id: 1, username: zhangsan }, timestamp: 1700000000000 }所有接口返回同一个结构前端只需处理一种格式。二、统一返回结果封装升级版1️⃣ 基础 Result 类Java 17 recordpublic record ResultT( int code, String message, T data, long timestamp ) { public static T ResultT success(T data) { return new Result(200, success, data, System.currentTimeMillis()); } public static T ResultT success() { return new Result(200, success, null, System.currentTimeMillis()); } public static T ResultT fail(int code, String message) { return new Result(code, message, null, System.currentTimeMillis()); } }2️⃣ 业务码枚举推荐public enum ResultCode { SUCCESS(200, success), BAD_REQUEST(400, 参数错误), UNAUTHORIZED(401, 未登录), FORBIDDEN(403, 无权限), NOT_FOUND(404, 资源不存在), INTERNAL_SERVER_ERROR(500, 服务器异常); private final int code; private final String message; ResultCode(int code, String message) { this.code code; this.message message; } public int code() { return code; } public String message() { return message; } }3️⃣ 增强 Result 支持枚举public record ResultT( int code, String message, T data, long timestamp ) { public static T ResultT success(T data) { return new Result(ResultCode.SUCCESS.code(), ResultCode.SUCCESS.message(), data, System.currentTimeMillis()); } public static T ResultT fail(ResultCode resultCode) { return new Result(resultCode.code(), resultCode.message(), null, System.currentTimeMillis()); } public static T ResultT fail(ResultCode resultCode, String customMessage) { return new Result(resultCode.code(), customMessage, null, System.currentTimeMillis()); } }三、全局异常处理核心1️⃣ RestControllerAdvice 是什么RestControllerAdvice public class GlobalExceptionHandler { }✅ 作用拦截所有RestController抛出的异常统一处理返回Result避免异常堆栈直接暴露给前端2️⃣ 处理参数校验异常ValidExceptionHandler(MethodArgumentNotValidException.class) public Result? handleValidException(MethodArgumentNotValidException e) { String message e.getBindingResult().getFieldErrors().stream() .map(error - error.getField() : error.getDefaultMessage()) .findFirst() .orElse(参数错误); return Result.fail(ResultCode.BAD_REQUEST, message); }3️⃣ 处理业务异常自定义异常定义业务异常public class BusinessException extends RuntimeException { private final ResultCode resultCode; public BusinessException(ResultCode resultCode) { super(resultCode.message()); this.resultCode resultCode; } public BusinessException(ResultCode resultCode, String message) { super(message); this.resultCode resultCode; } public ResultCode getResultCode() { return resultCode; } }处理业务异常ExceptionHandler(BusinessException.class) public Result? handleBusinessException(BusinessException e) { return Result.fail(e.getResultCode(), e.getMessage()); }4️⃣ 处理其他常见异常ExceptionHandler(Exception.class) public Result? handleException(Exception e) { log.error(系统异常, e); return Result.fail(ResultCode.INTERNAL_SERVER_ERROR); }四、完整全局异常处理器生产级模板RestControllerAdvice Slf4j public class GlobalExceptionHandler { ExceptionHandler(MethodArgumentNotValidException.class) public Result? handleValidException(MethodArgumentNotValidException e) { String message e.getBindingResult().getFieldErrors().stream() .map(error - error.getField() : error.getDefaultMessage()) .findFirst() .orElse(参数错误); return Result.fail(ResultCode.BAD_REQUEST, message); } ExceptionHandler(BindException.class) public Result? handleBindException(BindException e) { String message e.getBindingResult().getFieldErrors().stream() .map(error - error.getField() : error.getDefaultMessage()) .findFirst() .orElse(参数绑定错误); return Result.fail(ResultCode.BAD_REQUEST, message); } ExceptionHandler(MissingServletRequestParameterException.class) public Result? handleMissingParameterException(MissingServletRequestParameterException e) { return Result.fail(ResultCode.BAD_REQUEST, 缺少参数: e.getParameterName()); } ExceptionHandler(HttpMessageNotReadableException.class) public Result? handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { return Result.fail(ResultCode.BAD_REQUEST, 请求体格式错误); } ExceptionHandler(MethodArgumentTypeMismatchException.class) public Result? handleTypeMismatchException(MethodArgumentTypeMismatchException e) { return Result.fail(ResultCode.BAD_REQUEST, 参数类型不匹配: e.getName()); } ExceptionHandler(BusinessException.class) public Result? handleBusinessException(BusinessException e) { return Result.fail(e.getResultCode(), e.getMessage()); } ExceptionHandler(Exception.class) public Result? handleException(Exception e) { log.error(系统异常, e); return Result.fail(ResultCode.INTERNAL_SERVER_ERROR); } }五、业务异常实战示例Service 层抛出业务异常Service public class UserServiceImpl implements UserService { public UserVO getById(Long id) { User user userRepository.findById(id) .orElseThrow(() - new BusinessException(ResultCode.NOT_FOUND, 用户不存在)); return convertToVO(user); } }Controller 层无需 try-catchGetMapping(/users/{id}) public ResultUserVO getUser(PathVariable Long id) { return Result.success(userService.getById(id)); }✅ 异常自动被全局处理器捕获返回统一格式。六、HTTP 状态码 vs 业务码场景HTTP 状态码业务码参数错误400400未登录401401无权限403403资源不存在404404服务器异常500500推荐HTTP 状态码与业务码保持一致减少歧义。七、Spring Boot 4 中的新变化异常处理与 AOT 编译更兼容对 Jakarta Validation 支持更完善异常处理器加载更早启动更快八、常见坑位总结❌ 坑1返回格式不统一✅ 所有接口返回ResultT❌ 坑2异常直接抛给前端✅ 用RestControllerAdvice统一处理❌ 坑3吞掉异常不记录日志✅ 全局处理器中记录日志❌ 坑4业务异常用 RuntimeException✅ 自定义BusinessException❌ 坑5参数校验异常返回堆栈✅ 只返回友好错误信息九、本篇总结统一返回结果所有接口返回ResultT全局异常处理RestControllerAdvice业务异常自定义BusinessException参数校验统一处理MethodArgumentNotValidException生产级模板直接复制使用
返回列表