Spring Boot自定义异常触发时Rest Controller返回500错误排查
问题根源分析
你的Controller无法捕获DuplicateNumberException和NotFoundException、反而返回500错误的核心原因是:Service层的try-catch块捕获了所有Exception(包括你自定义的业务异常),并将其包装成RuntimeException抛出。这样Controller层只能捕获到RuntimeException,触发500内部服务器错误响应。
解决方案
1. 调整Service层的异常处理逻辑
修改Service中的try-catch块,让自定义业务异常跳过捕获,直接向上传递到Controller:
@Autowired private RecordRepository recordRepository; public void insertRecord(String dest, Integer num) { try { List<Record> b = recordRepository.findByDest(dest); ArrayList<Integer> numList = new ArrayList<>(); Record record = new Record(); if (!b.isEmpty()) { for (Record r : b) { numList.add(r.num()); } if (!numList.contains(num)) { List<Record> counts = recordRepository.findAll(); ArrayList<Integer> countList = new ArrayList<>(); for (Record r1 : counts) { countList.add(r1.getId()); } Collections.sort(countList); int countInsert = countList.get(countList.size() - 1); record.setId(countInsert + 1); record.setDest(dest); record.setNum(num); recordRepository.save(record); } else { throw new DuplicateNumberException("Number already exists."); } } else { throw new NotFoundException("No records found for the given String."); } } catch (DuplicateNumberException | NotFoundException e) { // 直接重新抛出自定义业务异常,不包装 throw e; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("An unexpected error occurred while processing the request.", e); } }
或者更简洁的方式:移除对自定义异常的捕获,业务异常本身就应由上层Controller处理,Service层仅处理非预期的系统异常:
@Autowired private RecordRepository recordRepository; public void insertRecord(String dest, Integer num) { List<Record> b = recordRepository.findByDest(dest); ArrayList<Integer> numList = new ArrayList<>(); Record record = new Record(); if (!b.isEmpty()) { for (Record r : b) { numList.add(r.num()); } if (!numList.contains(num)) { List<Record> counts = recordRepository.findAll(); ArrayList<Integer> countList = new ArrayList<>(); for (Record r1 : counts) { countList.add(r1.getId()); } Collections.sort(countList); int countInsert = countList.get(countList.size() - 1); record.setId(countInsert + 1); record.setDest(dest); record.setNum(num); recordRepository.save(record); } else { throw new DuplicateNumberException("Number already exists."); } } else { throw new NotFoundException("No records found for the given String."); } }
2. 确保自定义异常继承RuntimeException
你的自定义异常需要继承RuntimeException(Spring默认会处理RuntimeException的传递),示例:
public class DuplicateNumberException extends RuntimeException { public DuplicateNumberException(String message) { super(message); } } public class NotFoundException extends RuntimeException { public NotFoundException(String message) { super(message); } }
3. 优化ID生成逻辑(可选但重要)
你当前手动查询最大ID并+1的方式存在并发安全问题,多个请求同时执行时可能生成重复ID。建议在Record实体的id字段使用JPA自动生成策略:
@Entity public class Record { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) // 数据库自增ID private Integer id; // 其他字段和方法 }
这样就可以删除Service中手动计算ID的代码,直接调用recordRepository.save(record)即可,JPA会自动生成唯一ID。
验证修改
调整完成后,当触发DuplicateNumberException时,Controller会捕获到该异常并返回400 BAD_REQUEST;触发NotFoundException时返回404 NOT_FOUND,不再返回500错误。
内容的提问来源于stack exchange,提问作者Axel Blaze
相关产品推荐
相关产品推荐

