You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 03:50:08