后端框架错误处理常见问题与应对策略
开发中遇到接口返回500、页面突然打不开,很多时候不是网络问题,而是后端框架的错误处理没做好。比如用户提交表单时传了个非法参数,系统直接抛出空指针异常,日志里一堆堆栈信息,前端却只看到“服务器内部错误”,这种体验很糟糕。
很多开发者在写业务逻辑时只关注正常流程,忽略了异常分支。比如用Spring Boot写接口,一个findById方法查不到记录就直接返回null,上层没做判空,后续调用就会触发NullPointerException。这类问题其实在设计阶段就能避免。
统一异常拦截是基础
主流后端框架都支持全局异常处理。以Spring Boot为例,可以通过@ControllerAdvice定义一个统一的异常处理器:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseBody
public ErrorResponse handleNotFound(NotFoundException e) {
return new ErrorResponse("NOT_FOUND", e.getMessage());
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorResponse handleGeneric(Exception e) {
log.error("未预期异常:", e);
return new ErrorResponse("SERVER_ERROR", "服务器开小差了,请稍后再试");
}
}这样当业务代码抛出自定义的NotFoundException时,客户端收到的是结构化的错误信息,而不是一串HTML错误页。
别让数据库异常裸奔
执行SQL时出现主键冲突、字段超长等问题,框架通常会抛出DataAccessException。如果直接往上抛,调用方很难理解具体原因。应该在DAO层就做一次转换:
try {
jdbcTemplate.update(sql, params);
} catch (DuplicateKeyException e) {
throw new UserAlreadyExistsException("用户名已存在");
} catch (DataIntegrityViolationException e) {
throw new InvalidInputException("输入数据不符合规则");
}把底层技术性异常包装成业务语义更清晰的异常,上层处理起来也更方便。
线上系统最怕错误信息暴露敏感数据。曾经有个项目把数据库连接失败的异常直接返回给前端,结果错误信息里包含了数据库地址和账号,被爬虫抓走后导致数据泄露。生产环境一定要关掉详细的错误堆栈,自定义友好提示。
日志记录要留痕
错误处理不只是给用户看的,更是给开发者排查用的。每次捕获关键异常时,除了返回友好提示,还得把完整上下文写进日志:
log.warn("支付回调验证失败,订单号:{},来源IP:{}", orderId, clientIp);下次出问题,直接搜订单号就能定位到具体请求,不用再翻几十万行日志碰运气。
有些团队喜欢在控制器里写大量if-else判断参数合法性,代码又臭又长。其实可以用Bean Validation注解简化:
public class CreateUserRequest {
@NotBlank(message = "用户名不能为空")
private String username;
@Pattern(regexp = "^1[3-9]\d{9}$", message = "手机号格式不正确")
private String phone;
}配合全局异常处理,验证失败时自动抛出MethodArgumentNotValidException,并转为统一错误格式,省得每个接口重复写校验逻辑。
错误处理做得好,线上故障响应速度能提升一大截。用户看到清晰提示,运维能快速定位问题,开发也不用半夜被报警电话吵醒。看似是小事,实则影响整个系统的稳定性。