Rod*_*eas 8 java spring-mvc bean-validation spring-boot
我有一个 Spring @RestController,它的 POST 端点定义如下:
@RestController
@Validated
@RequestMapping("/example")
public class Controller {
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<?> create(@Valid @RequestBody Request request,
BindingResult _unused, // DO NOT DELETE
UriComponentsBuilder uriBuilder) {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
它还有一个异常处理程序javax.validation.ConstraintViolationException:
@ExceptionHandler({ConstraintViolationException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
ProblemDetails handleValidationError(ConstraintViolationException e) {...}
Run Code Online (Sandbox Code Playgroud)
我们的 Spring-Boot 应用程序用于spring-boot-starter-validation验证。该Request对象使用javax.validation.*注释将约束应用到各个字段,如下所示:
public class Request {
private Long id;
@Size(max = 64, message = "name length cannot exceed 64 characters")
private String name;
// ...
}
Run Code Online (Sandbox Code Playgroud)
如上所述,如果您使用无效请求 POST 请求,验证将抛出 ConstraintViolationException,该异常将由异常处理程序处理。这有效,我们对其进行了单元测试,一切都很好。
我注意到BindingResult没有使用 in the post 方法(名称_unused和评论//DO NOT DELETE有点危险信号。)我继续删除了参数。突然间,我的测试失败了——入站请求仍然经过验证,但它不再抛出 ConstraintValidationException ...现在它抛出MethodArgumentNotValidException!不幸的是,我无法使用这个其他异常,因为它不包含我需要的格式的失败验证(也不包含我需要的所有数据)。
为什么BindingResult参数列表中的存在控制抛出哪个异常?ConstraintViolationException当 javax.validation 确定请求正文无效时,如何删除未使用的变量并仍然抛出异常?
Spring-Boot 2.5.5
OpenJDK 17。
Ken*_*han 14
这里涉及两层验证,按以下顺序发生:
控制器层:
@RequestBodyor@ModelAttribute和@Validor@Validated或名称以“Valid”开头的任何注释进行注释时启用(请参阅此逻辑)。DataBinder东西BindingResult如果出现验证错误并且控制器方法中没有参数,则抛出异常org.springframework.web.bind.MethodArgumentNotValidException。BindingResult否则,继续使用捕获验证错误信息的参数调用控制器方法。Bean的方法层:
@Validated并且方法参数或返回值仅带有 bean 验证注释,则启用,例如@Valid,@Size等)MethodValidationInterceptorjavax.validation.ConstraintViolationException。最后两层中的验证将委托给 bean 验证来执行实际验证。
因为控制器实际上是一个 spring bean ,所以在调用控制器方法时,两层中的验证都会生效,您的案例完全证明了这一点,并发生以下情况:
DataBinder验证请求不正确,但由于控制器方法有BindingResult参数,因此它会跳过抛出MethodArgumentNotValidException并继续调用控制器方法
MethodValidationInterceptor验证请求不正确,并抛出ConstraintViolationException
文件并没有明确提及此类行为。以上是我阅读源码后总结的。我同意这很令人困惑,尤其是在您的情况下,当在两个层中都启用验证并且还与BindingResult。您可以看到 bean 验证实际上验证了请求两次,这听起来很尴尬......
因此,要解决您的问题,您可以禁用控制器层中的验证DataBinder并始终依赖于 bean 方法级别验证。
要对所有控制器全局禁用它,您可以@ControllerAdvice使用以下@InitBinder方法创建一个:
@ControllerAdvice
public class InitBinderControllerAdvice {
@InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(null);
}
}
Run Code Online (Sandbox Code Playgroud)
要仅对一个控制器禁用它,您可以将此@InitBinder方法添加到该控制器:
@RestController
@Validated
@RequestMapping("/example")
public class Controller {
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.setValidator(null);
}
}
Run Code Online (Sandbox Code Playgroud)
然后,即使从控制器方法中删除BindingResult,它也应该抛出ConstraintViolationException。
| 归档时间: |
|
| 查看次数: |
5410 次 |
| 最近记录: |