Spring MVC 控制器,如何在清空表单值时保留 BindingResult 错误

2024-02-16

我有一个使用 Spring MVC 控制器的 Web 表单。该表单由 Spring 验证。 当出现验证错误时,Spring 会显示相同的表单,其中预先填写了用户输入的值以及验证错误。

出于安全原因,我不希望表单预先填写用户输入的值,但我确实需要显示验证错误。

我怎样才能做到这一点?

我通过查看 Spring MVC 源代码并了解 BINDING_RESULT_KEY 是如何构建的来实现此行为。这是源代码。

然而,这是一个 hack,它可能会停止在 Spring MVC 的新版本上工作。

我如何正确实现这一目标?

package com.nespresso.ecommerce.naw.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.ObjectError;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.validation.Valid;


@Controller
@RequestMapping("/my_form")
public class MyFormController extends FrontEndController {
    final String MY_FORM_OBJECT_NAME = "myForm";
    final String BINDING_RESULT_KEY = BindingResult.MODEL_KEY_PREFIX + "myForm";

    @RequestMapping(method = RequestMethod.POST)
    public String post(@Valid @ModelAttribute("myForm") MyForm myForm, Errors errors, Model model) {
        if (errors.hasErrors()) {
            emptyMyFormWhileKeepingFormErrors(model);
            return "my_form";
        }

        return "redirect:/confirmation";
    }

    @ModelAttribute("myForm")
    public MyForm myForm() {
        return new MyForm();
    }

    private void emptyMyFormWhileKeepingFormErrors(Model model) {
        BeanPropertyBindingResult bindingResult = (BeanPropertyBindingResult) model.asMap().get(BINDING_RESULT_KEY);
        if (bindingResult == null) {
            return;
        }
        MyForm emptyForm = myForm();

        // set the empty form, so the form is not pre-filled with the previous values.
        // However, this clears the validation errors also
        model.addAttribute(MY_FORM_OBJECT_NAME, emptyForm);

        // re-attach the validation errors, and empty the rejectedValue
        BeanPropertyBindingResult updatedBindingResult = new BeanPropertyBindingResult(emptyForm, MY_FORM_OBJECT_NAME);
        for (ObjectError oe : bindingResult.getAllErrors()) {
            if (!(oe instanceof FieldError)) {
                updatedBindingResult.addError(oe);
            } else {
                FieldError fieldError = (FieldError) oe;

                String rejectedValue = null;   // that's the point, create a copy of the FieldError, emptying the rejectedValue;

                FieldError updatedFieldError = new FieldError(
                        MY_FORM_OBJECT_NAME,
                        fieldError.getField(),
                        rejectedValue,
                        fieldError.isBindingFailure(),
                        fieldError.getCodes(),
                        fieldError.getArguments(),
                        fieldError.getDefaultMessage());
                updatedBindingResult.addError(updatedFieldError);
            }
        }

        model.addAttribute(BINDING_RESULT_KEY, updatedBindingResult);
    }
}

编辑:添加另一个解决方案

您可以拥有一个空模型对象,同时保留以前的错误,只需通过以下方式将新值(从新初始化的对象)复制到模型属性中即可:org.springframework.beans.BeanUtils:

@RequestMapping(method = RequestMethod.POST)
public String post(@Valid @ModelAttribute("myForm") MyForm myForm, Errors errors, Model model) {
    if (errors.hasErrors()) {
        MyForm emptyForm = new MyForm();
        BeanUtils.copyProperties(emptyForm, myForm);
        return "my_form";
    }

    return "redirect:/confirmation";
}

这样,您仍然可以显示以前的错误,并且表单为空。

还有一个警告:如果我使用时工作正常<input>字段,但不与<form:input>spring 增强字段确实使用拒绝的值errors.

如果您想继续使用<form:input>字段,您将不得不创建另一个BindingResult对象并用当前错误初始化它,只需将拒绝的值设置为 null。您甚至可以重置所有字段或仅重置错误字段:

@RequestMapping(value = "/form", method=RequestMethod.POST)
public String update(@Valid @ModelAttribute Form form, BindingResult result, Model model) {
    if (result.hasErrors()) {
        // uncomment line below to reset all fields - by default only offending ones are
        //form = new Form();
        BeanPropertyBindingResult result2 = new BeanPropertyBindingResult(form, result.getObjectName());
        for(ObjectError error: result.getGlobalErrors()) {
            result2.addError(error);
        }
        for (FieldError error: result.getFieldErrors()) {
            result2.addError(new FieldError(error.getObjectName(), error.getField(), null, error.isBindingFailure(), error.getCodes(), error.getArguments(), error.getDefaultMessage()));
        }
        model.addAllAttributes(result2.getModel());
        return "my_form";
    }

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

Spring MVC 控制器,如何在清空表单值时保留 BindingResult 错误 的相关文章

随机推荐

  • Keras 中的回调异常 - Tensorflow 2.0 - Python

    以下代码在与 Keras 打包的 MNIST 数据上运行顺序 Keras 模型 非常简单 在运行以下代码时 我遇到异常 该代码很容易重现 import tensorflow as tf class myCallback tf keras c
  • 有没有办法获取 Visual Studio 使用的构建命令行?

    我想从命令行进行构建 但我想从 Visual Studio 2012 获取确切的命令行语法 这样我就不必手动找出所有标志 导入和其他参数 有没有办法让 Visual Studio 显示这些信息 在 Visual Studio 中生成项目或解
  • 使用 Service Worker 处理离线时的文件上传

    我们有一个 Web 应用程序 使用 AngularJS 构建 我们也逐渐添加 PWA 功能 服务工作线程 可启动项 通知等 我们的网络应用程序的功能之一是能够在离线状态下完成网络表单 目前 我们在离线时将数据存储在 IndexedDB 中
  • Mongoid embeds_many:推送文档而不保存以保留脏状态

    在 Mongoid 中 将文档推送到embeds many关系自动将文档保存到数据库中 通常 这很好 但是当我需要跟踪嵌入文档的更改时 我会遇到问题 假设您有两个模型 class List include Mongoid Document
  • 从函数 React Native 获取字符串结果

    我创建了一个连接到 redux 并返回当前加载的组件item sname 作为导航标题 但是当我尝试从此组件获取标题时 它会出现以下错误 Error title cannot be defined as a function in navi
  • 不支持的major.minor版本51.0(无法加载类org.postgresql.Driver)

    使用maven创建一个Web应用程序并将其部署在heroku上 一切正常 但当我调用使用 postgresql 9 2 1002 jdbc4 驱动程序的操作时 我得到 java lang UnsupportedClassVersionErr
  • Android Studio 3.5 原因:buildOutput.apkData 不得为 null Clean & Rebuild 已尝试

    在我将 Android Studio 3 4 更新到 3 5 并想要构建签名 APK 后 我收到错误 原因 buildOutput apkData 不能为 null 错误 我已经尝试过清理和制作项目以及清理和重建项目 但没有成功 我正在使用
  • 从 iOS 15.0 同步 CloudKit 后,CoreData 中的关系为零

    我正在为一个新应用程序开发 PoC 但我遇到了两个 CoreData 实体的问题InboxItem and CardSet 它们中的每一个都具有可选的 对一 关系 当我在模拟器中启动应用程序并创建这些实体时 模拟器数据库和 CloudKit
  • 如何将对象标记为由 GC(垃圾收集器)进行垃圾收集?

    在Java中 有没有办法markGC 下一次垃圾收集的对象clean up cycle 我听说设置一个对象null不再有效 不 你不能 如果另一个变量引用了它 您会期望发生什么 请注意 您无法设置object为 null 您只能设置一个va
  • 如何将 JsonConverter 与 JToken.ToObject<>() 方法一起使用?

    我正在将一个大型 JSON 文件成功读取到 JObjects 中 我要反序列化的类型之一具有 System Drawing Color 类型的属性 此属性的 JSON 具有表示颜色的整数值 当我尝试执行 ToObject 时 我得到 将值
  • 查找插入特定表的存储过程

    有没有办法找到存储过程在表中创建的条目 举例来说 存储过程A插入表A存储过程 B 插入表 A存储过程 C 插入表 B 我希望查询返回存储过程 A 和存储过程 B 的名称 我现在已经得到了这个 但它所做的只是找到存储过程 我认为这将是查找存储
  • 暂时禁用mysql远程访问

    我需要在 Linux 服务器上的每晚 mysql 维护期间禁用远程访问 以便在此期间没有人可以查询数据库 我无法执行 SERVICE MYSQL STOP 因为那样我就无法执行我需要执行的操作 截断并重建几个表 有没有办法暂时关闭外部访问
  • 如何避免 R Fisher.test 工作区错误

    我正在对大量列联表进行渔夫精确检验 并保存生物信息学问题的 p val 其中一些列联表很大 因此我尽可能地增加了工作空间 但是当我运行以下代码时出现错误 result lt fisher test data workspace 2e9 LD
  • 命令行流网络摄像头,带有来自 Ubuntu 服务器的 WebM 格式的音频

    我正在尝试从连接到无头 Ubuntu 服务器 运行 Maverick 10 10 的网络摄像头传输视频和音频 我希望能够以 WebM 格式 VP8 视频 OGG 进行流式传输 带宽有限 因此流必须低于 1Mbps 我尝试过使用 FFmpeg
  • 获取用户使用discord.py发送的最后一条消息?

    我想知道是否有办法让机器人使用 Python 中的discord py 获取用户在服务器聊天中发送的最后一条消息 多谢 旧答案discord py async 重写前 Use log froms http discordpy readthe
  • Scala 关闭挂钩从未运行?

    scaladoc 为sys addShutdownHook says shutdown hooks are NOT guaranteed to be run 现在这是完全合理的 因为如果您向 JVM 发送 SIGKILL 或任何 Windo
  • 整数数组的ArrayList

    我正在尝试编写一个简单的游戏 其中敌人在网格上追逐玩家 我正在使用维基百科寻路页面上的简单寻路算法 这涉及创建两个列表 每个列表项包含 3 个整数 这是我尝试构建和显示这样一个列表的测试代码 当我运行以下代码时 它会为 ArrayList
  • R中随机选择一个样本

    我必须找到一种方法从集合 S 中随机选择 R 中的一个样本 集合 S 由 I1 集合中的样本组成 I1 lt c 1 2 3 4 5 6 s1 lt c 1 2 1 s2 lt c 1 5 6 s3 lt c 2 1 1 s4 lt c 5
  • JavaScript 中数组与对象的效率

    我有一个可能包含数千个对象的模型 我想知道存储它们并在获得单个对象的 id 后检索该对象的最有效方法是什么 id 是长数字 这是我正在考虑的两个选择 在选项一中 它是一个带有递增索引的简单数组 在选项 2 中 它是一个关联数组 也可能是一个
  • Spring MVC 控制器,如何在清空表单值时保留 BindingResult 错误

    我有一个使用 Spring MVC 控制器的 Web 表单 该表单由 Spring 验证 当出现验证错误时 Spring 会显示相同的表单 其中预先填写了用户输入的值以及验证错误 出于安全原因 我不希望表单预先填写用户输入的值 但我确实需要