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

2024-06-25

我有一个春天@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参数列表中的存在控制抛出哪个异常?如何删除未使用的变量并仍然抛出ConstraintViolationExceptionjavax.validation何时判断请求体无效?


Spring-Boot 2.5.5

  • spring-boot-启动器-web
  • spring-boot-starter-验证

OpenJDK 17。


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

  1. 控制器层 :

    • 当控制器方法的参数注释为时启用@RequestBody or @ModelAttribute@Valid or @Validated或任何名称以“Valid”开头的注释(请参阅this https://github.com/spring-projects/spring-framework/blob/e4e667acdb774b45164f3ec345bfdf9e406e537f/spring-context/src/main/java/org/springframework/validation/annotation/ValidationAnnotationUtils.java#L45对于逻辑)。
    • 基于DataBinder https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder stuff
    • 只能验证请求
    • 如果出现验证错误并且没有BindingResult控制器方法中的参数,抛出org.springframework.web.bind.MethodArgumentNotValidException。否则,继续调用控制器方法BindingResult捕获验证错误信息的参数。
  2. Bean的方法层 :

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

最后两层中的验证将委托给 bean 验证来执行实际验证。

因为控制器实际上是一个 spring bean ,所以在调用控制器方法时,两层中的验证都会生效,您的案例完全证明了这一点,并发生以下情况:

  1. DataBinder验证请求不正确,但由于控制器方法有BindingResult参数,它跳过抛出MethodArgumentNotValidException并继续调用控制器方法

  2. MethodValidationInterceptor验证请求不正确,并抛出ConstraintViolationException

文件并没有明确提及此类行为。以上是我阅读源码后总结的。我同意这很令人困惑,尤其是在您的情况下,当在两个层中都启用验证并且还与BindingResult争论。您可以看到 bean 验证实际上验证了请求两次,这听起来很尴尬......

因此,要解决您的问题,您可以禁用控制器层的验证DataBinder并且始终依赖于 bean 方法级别验证。

要对所有控制器全局禁用它,您可以创建一个@ControllerAdvice与以下@InitBinder method:

@ControllerAdvice
public class InitBinderControllerAdvice {

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

要仅对一个控制器禁用它,您可以添加此@InitBinder该控制器的方法:

@RestController
@Validated
@RequestMapping("/example")
public class Controller {

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

}

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

BindingResult 方法参数的存在决定了抛出异常吗? 的相关文章

  • 在记录活动中使用 readConfiguration 方法

    为了在小型 Java 桌面应用程序中使用日志记录 我试图深入了解一些方法的操作 我使用一个非常愚蠢的小Java程序来测试它们 特别是 在测试 LogManager readConfiguration 方法的行为时 我发现了一些奇怪的事情 在
  • RSA Java 加密和 Node.js 解密不起作用

    我有一个系统 需要在 javascript 中生成 RSA 密钥对 然后将公钥存储在服务器端的数据库中 作为字符串 然后 Java 中的服务器端将使用存储的公钥对字符串进行加密密钥并将其发送到客户端 客户端将使用私钥解密该字符串 我在客户端
  • 使用 org.eclipse.xsd 和 Maven2 分析 XML 模式

    我正在尝试实现示例代码本文 http help eclipse org help32 index jsp topic org eclipse xsd doc references articles dwtip1 scpw index htm
  • 为什么这个 Java 静态字段为空?

    public class StaticTest private static String a private static String b this is a public static void main String args a
  • HQL - 分页的行标识符

    有谁知道HQL是否有一个关键字来标识行 例如ROWID或ROWNUM 我想使用 HQL 实现分页 但我无法使用 setMaxResult 或 setFirstResult 因为我不直接使用会话对象 因此不使用 Query 对象 而只是将查询
  • 在java中将HTML转换为RTF?

    我需要将 HTML 转换为 RTF 我正在使用以下代码 private static String convertToRTF String htmlStr OutputStream os new ByteArrayOutputStream
  • 问题:将大数据传递给第二个 Activity

    我有一个奇怪的问题 我在网上浏览但没有找到答案 我仍然是android编程的初学者 那么让我们开始吧 我想做的就是用一些数据调用第二个活动 它适用于小数据 但如果数据变大 第二个 Activity 将不会显示 第一个 Activity 将完
  • 如何将堆栈跟踪转换为字符串?

    转换结果的最简单方法是什么Throwable getStackTrace 到描述堆栈跟踪的字符串 Use Throwable printStackTrace PrintWriter pw https docs oracle com java
  • 如何从 ByteBuffer 转换为 Integer 和 String?

    我转换了一个int使用 ByteBuffer 的字节数组putInt 方法 我该如何做相反的事情 那么将这些字节转换为 int 吗 此外 我使用 String 的将字符串转换为字节数组getBytes 方法 我如何将其反过来转换 这byte
  • 使用 Hibernate/Spring 生成数据库更新脚本

    我有一个项目 我们过去依赖 hibernate 来更新数据库 hibernate hbm2ddl auto update 即使在产品上 我正在将其迁移为使用 liquibase 我唯一担心的是 并不是我的团队中的每个人都是 sql 专家 因
  • java应用程序,线程在终止MySQL连接后挂起

    我有一些工作线程正在运行 其中包括 MySQL 和 mysql connector java 5 1 20 当我杀死一些 SQL 语句 使用 mysql 客户端的kill 连接id 时 java线程挂起 这应该抛出一些异常 jstack 打
  • 识别包含本机方法实现的库文件/源

    如何识别包含本机方法实现的库文件 Ex public native String intern 我在哪里可以找到实施 source code of String intern 方法 找到了答案String intern 与快速谷歌搜索 ht
  • Java Reflection:为什么这么慢?

    我一直避免使用 Java 反射 因为它速度缓慢 我在当前项目的设计中达到了一个点 能够使用它将使我的代码更具可读性和优雅性 所以我决定尝试一下 我只是对这种差异感到惊讶 我注意到有时运行时间几乎延长了 100 倍 即使在这个简单的例子中 它
  • Java XPath API - 获取表示子树的字符串

    我的问题不是关于 xpath 语法 而是与 xpath 周围的 java API 有关 考虑以下 xml
  • 在同步子句中抛出异常的副作用?

    从同步子句中抛出异常是否有任何不清楚的副作用 锁会发生什么情况 private void doSomething throws Exception synchronized lock doSomething 我没有看到任何副作用 The 锁
  • 在 libgdx 中截取屏幕截图

    我有一个应用程序 我想在其中截取游戏屏幕的屏幕截图并将其保存为图像并上传到 Facebook 我正在使用 Libgdx 我的重点是 android 谁能帮助我如何以编程方式截取游戏屏幕并将其另存为图像 现在相当容易 Libgdx提供了一个例
  • SwingWorker 和 Executor 的区别

    我正在使用 SwingWorker 在我正在制作的应用程序上执行一些重负载任务 虽然今天我遇到了 Executor 类和这个例子 Executors newCachedThreadPool execute new Runnable publ
  • Java中有没有办法随机获取HashMap的值?

    Java中有没有办法随机获取HashMap的值 这有效 Random generator new Random Object values myHashMap values toArray Object randomValue values
  • 解决SecurityException:权限拒绝:启动意图。我需要什么许可?

    我想从应用程序打开游戏商店 在三星手机上还好 但在一加手机上却失败了 我不知道在哪里alibaba来自 真奇怪 异常 java lang SecurityException 权限拒绝 启动意图 act android intent acti
  • Java:当计时器处于活动状态时,JSplitPane 将顶部面板的内容复制到底部面板

    所以我有一个 JSplitPane 和两个 JPanel 一个在顶部 一个在底部 在这两个面板中 我重写了paintComponent方法并添加了我自己的图形 在底部面板中 我想添加动画 当面板不重新绘制时 这很好 但是一旦计时器 java

随机推荐