Spring Boot通用异常无法返回ErrorDTO问题的解决方法
问题修复方案
原因分析
出现HttpMediaTypeNotAcceptableException是因为数据完整性约束异常(比如字段超长)触发时,没有被Spring MVC全局异常处理器及时捕获,转而被容器默认错误机制接管,导致响应媒体类型协商失败,无法返回自定义ErrorDTO。另外也可能是异常处理器的匹配范围不足、ErrorDTO序列化配置有问题。
修复步骤
1. 扩展全局异常处理器的异常捕获范围
修改GlobalExceptionHandler,明确捕获JPA/Hibernate抛出的数据完整性违规异常:
@RestControllerAdvice public class GlobalExceptionHandler { // 捕获数据完整性约束异常 @ExceptionHandler(DataIntegrityViolationException.class) public ResponseEntity<ErrorDTO> handleDataIntegrityViolation(DataIntegrityViolationException ex) { String errorMsg = "字段长度超出限制或违反数据完整性约束"; // 解析底层SQL异常获取更具体信息 if (ex.getCause() instanceof SQLException sqlEx) { errorMsg = sqlEx.getMessage(); } ErrorDTO errorDTO = new ErrorDTO(HttpStatus.BAD_REQUEST.value(), errorMsg, LocalDateTime.now()); return new ResponseEntity<>(errorDTO, HttpStatus.BAD_REQUEST); } // 保留通用异常处理 @ExceptionHandler(Exception.class) public ResponseEntity<ErrorDTO> handleGenericException(Exception ex) { ErrorDTO errorDTO = new ErrorDTO(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage(), LocalDateTime.now()); return new ResponseEntity<>(errorDTO, HttpStatus.INTERNAL_SERVER_ERROR); } }
2. 确保ErrorDTO可正常序列化
检查ErrorDTO的构造方法和getter/setter是否齐全,保证Jackson能正确序列化:
import java.time.LocalDateTime; public class ErrorDTO { private int status; private String message; private LocalDateTime timestamp; // 必须包含无参构造方法 public ErrorDTO() {} public ErrorDTO(int status, String message, LocalDateTime timestamp) { this.status = status; this.message = message; this.timestamp = timestamp; } // 所有字段的getter/setter public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public LocalDateTime getTimestamp() { return timestamp; } public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; } }
3. 禁用容器默认错误页面
在application.properties中添加配置,让Spring MVC优先使用自定义异常处理器:
server.error.whitelabel.enabled=false spring.mvc.throw-exception-if-no-handler-found=true spring.web.resources.add-mappings=false
4. 提前验证请求参数(可选优化)
在Controller层通过JSR-380注解提前校验字段长度,避免触发底层数据库异常:
@PostMapping("/v1/locations") public ResponseEntity<Location> createLocation(@Valid @RequestBody LocationRequest request) { // 业务逻辑实现 } // 请求参数DTO public class LocationRequest { @Size(max = 10, message = "code字段长度不能超过10位") private String code; // 其他字段及getter/setter }
同时在全局异常处理器中添加校验异常的处理:
@ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ErrorDTO> handleValidationExceptions(MethodArgumentNotValidException ex) { String errorMsg = ex.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(", ")); ErrorDTO errorDTO = new ErrorDTO(HttpStatus.BAD_REQUEST.value(), errorMsg, LocalDateTime.now()); return new ResponseEntity<>(errorDTO, HttpStatus.BAD_REQUEST); }
验证修复
重启项目后,再次发送超长code字段的POST请求,此时会返回自定义的ErrorDTO格式响应,控制台也不会再出现HttpMediaTypeNotAcceptableException异常。
内容的提问来源于stack exchange,提问作者Sercan Noyan Germiyanoğlu
相关产品推荐
相关产品推荐

