我有一个如下所示的异常类
@ResponseStatus(value=HttpStatus.UNPROCESSABLE_ENTITY, reason="Unprocessable Entity") // 422
public class UnprocessableEntityException extends RuntimeException {
}
现在状态不会返回 422,除非我在 Controller 类中编写特定的处理程序,例如:
@ExceptionHandler(UnprocessableEntityException.class)
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
public String handleException(Exception ex) {
...
}
据我了解,我首先不需要@ExceptionHandler,不确定我错过了什么。
从控制器方法抛出
@ResponseStatus
带注释的异常应该足以让框架编写 HTTP 状态代码 - 不需要 @ExceptionHandler
。
以下内容将按预期在访问 webapp 根目录时写入 422 状态:
@Controller
public class ExceptionController {
@RequestMapping("/")
public void action() {
throw new ActionException();
}
@ResponseStatus(value = HttpStatus.UNPROCESSABLE_ENTITY, reason = "nope")
public static class ActionException extends RuntimeException {}
}
这得益于 Spring MVC 默认创建的
ResponseStatusExceptionResolver
- 如果它不适合您,我的猜测是这个默认异常解析器已被删除(例如通过覆盖 WebMvcConfigurationSupport.configureHandlerExceptionResolvers
或以其他方式配置您的上下文的 HandlerExceptionResolver
)
这样ResponseStatusExceptionResolver
就被压倒了。)
抛出的异常不应由代码或其他异常解析器处理,例如不应由
@ExceptionHandler
处理,因为这将覆盖异常类的 @ResponseStatus
指定的状态代码。
使用从服务层使用的
ResponseStatus
注释异常类并不是一个好的做法。
最好的解决方案是通过扩展
Exception' or
RuntimeException'来创建自己的异常类。然后使用 ControllerAdvice
注释创建一个全局异常处理程序,您可以在其中轻松设置每个异常的 HTTP 响应状态代码。
如果要回滚 JPA 事务,请扩展
RuntimeException' because the Spring
@Repository' 默认情况下仅在这种情况下进行数据库回滚。
自定义异常类示例:
public class AlreadyRegisteredException extends RuntimeException {
public AlreadyRegisteredException(final String message) {
super(message);
}
}
public class EntityNotFoundException extends RuntimeException {
public EntityNotFoundException(final String message) {
super(message);
}
}
全局异常处理类:
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(AlreadyRegisteredException.class)
public ResponseEntity<String> handleAlreadyRegisteredException(AlreadyRegisteredException ex) {
log.error(ex);
return new ResponseEntity<>(ex.getMessage(), HttpStatus.ALREADY_REPORTED);
}
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<String> handleEntityNotFoundException(EntityNotFoundException ex) {
var reason = ex.getMessage();
log.warn(reason);
return new ResponseEntity<>(reason, HttpStatus.NOT_FOUND);
}
}
用途:
休息终点
@Slf4j
@RestController
@RequestMapping("/customer")
@RequiredArgsConstructor
public class CustomerController {
private final CustomerService customerService;
@PostMapping("/register")
public void registerByEmail(@RequestBody Customer customer) {
customerService.register(customer);
}
}
服务层
@Component
@Slf4j
@RequiredArgsConstructor
public class CustomerService {
private final CustomerRepository customerRepository;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void register(Customer customer) {
Optional<String> email = getEmail(customer);
if (email.isEmpty()) {
throw new EmptyIdException("Unable to register a new customer. Customer's email is null.");
}
Optional<CustomerEntity> existingCustomer = customerRepository.findByEmail(Id.get());
if (existingCustomer.isPresent()){
throw new AlreadyRegisteredException(String.format("Unable to register a new customer. Customer with the "
+ "same email has already been registered: {email: \"%s\"}", email.get()));
} else {
customerRepository.save(...);
}
}
}
希望对您有帮助。