当验证失败时,在 Spring RestController 中引发什么类型的异常?

在 Spring 中,我通过简单地将相应的方法参数注释为 or 来输入验证 。其他一些验证只能在对传入数据进行一些处理后执行。我的问题是,我应该使用什么类型的异常,以便它类似于注释引发的异常,以及如何从验证结果构造此异常。下面是一个示例:RestControllerRequestBody@Valid@Validated@Valid

@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> createOrder(@RequestBody @Validated(InputChecks.class) Order order) {
    // Some processing of the Order goes here
    Set<ConstraintViolation<Order>> violations = validator.validate(order, FinalChecks.class);
    // What to do now with the validation errors?
    orders.put(order);
    HttpHeaders headers = new HttpHeaders();
    headers.setLocation(ServletUriComponentsBuilder.fromCurrentRequest().path("/" + order.getId()).build().toUri());
    return new ResponseEntity<>(null, headers, HttpStatus.CREATED);
}

答案 1

对我来说,最简单的方法看起来像是用错误对象验证对象,并在MethodArgumentNotValidException中使用它。

@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> createOrder(@RequestBody @Validated(InputChecks.class) Order order)
                throws NoSuchMethodException, SecurityException, MethodArgumentNotValidException {
    // Some processing of the Order goes here
    SpringValidatorAdapter v = new SpringValidatorAdapter(validator);
    BeanPropertyBindingResult errors = new BeanPropertyBindingResult(order, "order");
    v.validate(order, errors, FinalChecks.class);
    if (errors.hasErrors()) {
        throw new MethodArgumentNotValidException(
                new MethodParameter(this.getClass().getDeclaredMethod("createOrder", Order.class), 0),
                errors);
    }
    orders.put(order);
    HttpHeaders headers = new HttpHeaders();
    headers.setLocation(ServletUriComponentsBuilder.fromCurrentRequest().path("/" + order.getId()).build().toUri());
    return new ResponseEntity<>(null, headers, HttpStatus.CREATED);
}

这样,在第二个验证步骤中发现的错误与在@validated参数的输入验证期间发现的错误具有完全相同的结构。


答案 2

为了在第二次运行中处理验证错误,我可以想到三种不同的方法。首先,您可以从 s 中提取验证错误消息,然后返回相应的 HTTP 响应,例如 ,以验证错误消息作为响应正文:SetConstraintViolation400 Bad Request

Set<ConstraintViolation<Order>> violations = validator.validate(order, FinalChecks.class);
if (!violations.isEmpty()) {
    Set<String> validationMessages = violations
                                     .stream()
                                     .map(ConstraintViolation::getMessage)
                                     .collect(Collectors.toSet());

    return ResponseEntity.badRequest().body(validationMessages);
}
// the happy path

此方法适用于需要对几个控制器进行双重验证的情况。否则,最好抛出一个全新的或重用与弹簧相关的异常,比如说,并定义一个普遍处理它们的异常:ExceptionMethodArgumentNotValidExceptionControllerAdvice

Set<ConstraintViolation<Order>> violations = validator.validate(order, FinalChecks.class);
if (!violations.isEmpty()) {
    throw new ValidationException(violations);
}

控制器建议:

@ControllerAdvice
public class ValidationControllerAdvice {
    @ExceptionHandler(ValidationException.class)
    public ResponseEntity handleValidtionErrors(ValidationException ex) {
        return ResponseEntity.badRequest().body(ex.getViolations().stream()...);
    }
}

您还可以抛出一个弹簧异常,例如.为此,您需要将 s 转换为 的实例,并将其传递给 的构造函数。MethodArgumentNotValidExceptionSetConstraintViolationBindingResultMethodArgumentNotValidException


推荐