带有自定义异常类的Spring REST @ResponseStatus不会更改返回状态代码

问题描述 投票:0回答:3

我有一个如下所示的异常类

@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,不确定我错过了什么。

java spring-rest
3个回答
3
投票

从控制器方法抛出

@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
就被压倒了。)


1
投票

抛出的异常不应由代码或其他异常解析器处理,例如不应由

@ExceptionHandler
处理,因为这将覆盖异常类的
@ResponseStatus
指定的状态代码。


0
投票

使用从服务层使用的

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(...);
        }
    }
}

希望对您有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.