java 验证失败时在 Spring RestController 中抛出什么类型的异常?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35766872/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 00:34:05  来源:igfitidea点击:

What type of exception to throw in Spring RestController when validation fails?

javaspringvalidationexceptionspring-restcontroller

提问by Gregor

In a Spring RestControllerI have an input validation of the RequestBodysimply by annotating the corresponding method parameter as @Validor @Validated. Some other validations can only be performed after some processing of the incoming data. My question is, what type of exceptions should I use, so that it resembles the exception thrown by the @Validannotation, and how do I construct this exception from the validation result. Here is an example:

在 Spring 中,RestControllerRequestBody只需将相应的方法参数注释为@Validor即可进行输入验证@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);
}

回答by Gregor

To me the simplest way looks like validating the object with an errors object, and use it in a MethodArgumentNotValidException.

对我来说,最简单的方法看起来像是使用错误对象验证对象,并在 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);
}

This way the errors found during the second validation step have exactly the same structure as the errors found during the input validation on the @validated parameters.

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

回答by Ali Dehghani

For handling validation errors in the second run, i can think of three different approaches. First, you can extract validation error messages from Setof ConstraintViolations and then return an appropriate HTTP response, say 400 Bad Request, with validation error messages as the response body:

为了在第二次运行中处理验证错误,我可以想到三种不同的方法。首先,您可以从Setof 中提取验证错误消息ConstraintViolation,然后返回适当的 HTTP 响应,例如400 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

This approach is suitable for situations when the double validation is a requirement for a few controllers. Otherwise, it's better to throw a brand new Exceptionor reuse spring related exceptions, say MethodArgumentNotValidException, and define a ControllerAdvicethat handle them universally:

这种方法适用于少数控制器需要双重验证的情况。否则,最好抛出一个全新的Exception或重用与 spring 相关的异常,例如MethodArgumentNotValidException,并定义一个ControllerAdvice通用处理它们的异常:

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

And the controller advice:

和控制器的建议:

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

You can also throw one of spring exceptions like MethodArgumentNotValidException. In order to do so, you need to convert the Setof ConstraintViolations to an instance of BindingResultand pass it to the MethodArgumentNotValidException's constructor.

您还可以抛出弹簧异常之一,例如MethodArgumentNotValidException. 为此,您需要将Setof ConstraintViolations转换为of的实例BindingResult并将其传递给MethodArgumentNotValidException的构造函数。