gpt4 book ai didi

java - BindingResult 方法参数的存在决定了抛出的异常?

转载 作者:行者123 更新时间:2023-12-04 12:21:04 30 4
gpt4 key购买 nike

bounty 4 天后到期。这个问题的答案有资格获得 +50 声望奖励。
Roddy of the Frozen Peas想引起对这个问题的更多关注。







我有一个 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) {
// ...
}
}
它还有一个 javax.validation.ConstraintViolationException 的异常处理程序。 :
@ExceptionHandler({ConstraintViolationException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
ProblemDetails handleValidationError(ConstraintViolationException e) {...}
我们的 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;

// ...
}
如上所述,如果您使用无效请求 POST 请求,验证将抛出 ConstraintViolationException,该异常将由异常处理程序处理。这有效,我们有单元测试,一切都很好。
我注意到 BindingResult在 post 方法中没有使用(名称 _unused 和评论 //DO NOT DELETE 是一种危险信号。)我继续删除了参数。突然间,我的测试失败了——入站请求仍然被验证,但它不再抛出 ConstraintValidationException ......现在它抛出一个 MethodArgumentNotValidException !不幸的是,我不能使用这个其他异常,因为它不包含我需要的格式的失败验证(也不包含我需要的所有数据)。
为什么 BindingResult参数列表中的存在控制抛出哪个异常?如何删除未使用的变量并仍然抛出 ConstraintViolationException当 javax.validation 确定请求正文无效时?

Spring 启动 2.5.5
  • spring-boot-starter-web
  • spring-boot-starter-validation

  • 17. OpenJDK

    最佳答案

    这里涉及两层验证,它们按以下顺序发生:

  • Controller 层 :
  • 当 Controller 方法的参数用 @RequestBody 注释时启用或 @ModelAttribute并与 @Valid@Validated或任何名称以“Valid”开头的注释(有关逻辑,请参阅 this)。
  • 基于 DataBinder 东西
  • 只能验证请求
  • 如果验证错误并且没有 BindingResult Controller 方法中的参数,抛出 org.springframework.web.bind.MethodArgumentNotValidException .否则,继续使用 BindingResult 调用 Controller 方法使用验证错误信息捕获的参数。

  • Bean 的方法层 :
  • 如果使用 @Validated 注释,则启用 spring bean并且方法参数或返回值仅使用 bean 验证注释进行注释,例如 @Valid , @Size
  • 基于 AOP 的东西。方法拦截器是MethodValidationInterceptor
  • 可以验证请求和响应
  • 如果验证错误,抛出 javax.validation.ConstraintViolationException .


  • 最后两层中的验证将委托(delegate)给 bean 验证来执行实际验证。
    因为 Controller 实际上是一个 spring bean,所以在调用 Controller 方法时,两层中的验证都可以生效,这在您的案例中完全展示了,发生以下事情:
  • DataBinder验证请求不正确,但由于 Controller 方法具有 BindingResult参数,它跳过抛出 MethodArgumentNotValidException并继续调用 Controller 方法
  • MethodValidationInterceptor验证请求不正确,并抛出 ConstraintViolationException

  • 文件没有明确提到这种行为。我是在阅读源代码后做出上述总结的。我同意这会令人困惑,尤其是在您的情况下,当验证在两个层以及 BindingResult 中都启用时争论。您可以看到 bean 验证实际上对请求进行了两次验证,这听起来很尴尬......
    因此,为了解决您的问题,您可以在 Controller 层的 DataBinder 中禁用验证。并且始终依赖于 bean 方法级别的验证。您可以通过创建 @ControllerAdvice 来实现与以下 @InitBinder方法:
    @ControllerAdvice
    public class InitBinderControllerAdvice {

    @InitBinder
    private void initBinder(WebDataBinder binder) {
    binder.setValidator(null);
    }
    }

    然后甚至删除 BindingResult从 Controller 方法中,它也应该抛出 ConstraintViolationException .

    关于java - BindingResult 方法参数的存在决定了抛出的异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69654961/

    30 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com