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
What type of exception to throw in Spring RestController when validation fails?
提问by Gregor
In a Spring RestController
I have an input validation of the RequestBody
simply by annotating the corresponding method parameter as @Valid
or @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 @Valid
annotation, and how do I construct this exception from the validation result. Here is an example:
在 Spring 中,RestController
我RequestBody
只需将相应的方法参数注释为@Valid
or即可进行输入验证@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 Set
of ConstraintViolation
s and then return an appropriate HTTP response, say 400 Bad Request
, with validation error messages as the response body:
为了在第二次运行中处理验证错误,我可以想到三种不同的方法。首先,您可以从Set
of 中提取验证错误消息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 Exception
or reuse spring related exceptions, say MethodArgumentNotValidException
, and define a ControllerAdvice
that 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 Set
of ConstraintViolation
s to an instance of BindingResult
and pass it to the MethodArgumentNotValidException
's constructor.
您还可以抛出弹簧异常之一,例如MethodArgumentNotValidException
. 为此,您需要将Set
of ConstraintViolation
s转换为of的实例BindingResult
并将其传递给MethodArgumentNotValidException
的构造函数。