微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

问题以Spring MVC Thymeleaf格式显示验证错误

如何解决问题以Spring MVC Thymeleaf格式显示验证错误

我正在构建一个Spring MVC应用程序,其中包括一个控制器,该控制器可以为Thymeleaf构建的许多不同形式的请求提供服务。

我试图通过一个简化的示例来说明以下问题,该示例包含两种非常简单的形式-第一种要求输入名字,第二种要求电子邮件地址:

<form action="#" th:action="@{${contextpath}}" th:object="${formBean}" method="post">
    <div>
        <span th:if="${#fields.hasGlobalErrors()}" th:text="#{message.errorsFound}">Errors found</span>
    </div>
    <div>
        <!--div>
            <span th:if="#{fields.hasErrors(__${items['firstName']}__}" th:error="*{items['firstName']}">First name error</span>
        </div-->
        <label th:for="firstName" th:text="#{form.firstName}" >First Name</label>
        <input type="text" th:field="*{items['firstName']}" />
    </div>
    <div>
        <input type="submit" value="Submit" th:value="#{form.submitLabel}" />
    </div>
</form>

<form action="#" th:action="@{${contextpath}}" th:object="${formBean}" method="post">
    <div>
        <span th:if="${#fields.hasGlobalErrors()}" th:text="#{message.errorsFound}">Errors found</span>
    </div>
    <div>
        <label th:for="email" th:text="#{form.email}" >Email</label>
        <input type="text" th:field="*{items['email']}" />
    </div>
    <div>
        <input type="submit" value="Submit" th:value="#{form.submitLabel}" />
    </div>
</form>

我正在使用包含Map<String,Object>的模型类:

@Data
public class DynamicFormBean {

    Map<String,Object> items = new HashMap<>();

    public void initialiseField(String key) {
        items.put(key,"");
    }
}

“快乐的道路”完美工作-将表单数据映射到模型,并在输入通过验证后返回的视图中显示期望的信息。

提供无效输入时,会发生此问题。我正在重新显示表单视图,但是未显示预期的错误消息。

验证逻辑通过Errors使用Spring BindingResult接口:

        Map<String,Object> formFields = formBean.getItems();
        String fieldName = "";
        String fieldValue = "";
        if (viewPath.contains("page1")) {
            fieldName = "firstName";
            fieldValue = (String)formFields.get(fieldName);
        } else if (viewPath.contains("page2")) {
            fieldName = "email";
            fieldValue = (String) formFields.get(fieldName);
        }
        if (StringUtils.isEmpty(fieldValue)) {
            result.rejectValue(fieldName,MessageCodes.FIELD_IS_required);
        } else if (fieldValue.length() < 3) {
            result.rejectValue(fieldName,MessageCodes.FIELD_TOO_SHORT);
        } else if (fieldValue.length() > 20) {
            result.rejectValue(fieldName,MessageCodes.FIELD_TOO_LONG);
        }
        if (result.hasErrors()) {
            model.addAttribute("formBean",formBean);
            result.reject(MessageCodes.ERRORS_FOUND,"Default message");
            return viewPath;
        }

我尝试了各种机制来按我想要的方式工作。

首先,在控制器的post方法签名中声明一个BindingResult

    @PostMapping(path = "/**")
    public String processForm(final HttpServletRequest request,final DynamicFormBean formBean,BindingResult result,Model model) {

但是,这不起作用,因为Spring注入了BeanPropertyBindingResult的实例,但是它不支持访问存储在Map中的bean属性

org.springframework.beans.NotReadablePropertyException: Invalid property 'firstName' of bean class [com.xxx.pts.acscustompages.model.form.DynamicFormBean]: Bean property 'firstName' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
    at org.springframework.beans.AbstractnestablePropertyAccessor.getPropertyValue(AbstractnestablePropertyAccessor.java:622) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.beans.AbstractnestablePropertyAccessor.getPropertyValue(AbstractnestablePropertyAccessor.java:612) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.validation.AbstractPropertyBindingResult.getActualFieldValue(AbstractPropertyBindingResult.java:104) ~[spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.validation.AbstractBindingResult.rejectValue(AbstractBindingResult.java:117) ~[spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.validation.AbstractErrors.rejectValue(AbstractErrors.java:128) ~[spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at com.xxx.pts.acscustompages.controller.DynamicFormController.processForm(DynamicFormController.java:52) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]

我发现我需要改用MapBindingResult。但是,如果我在控制器方法签名中指定该类:

    @PostMapping(path = "/**")
    public String processForm(final HttpServletRequest request,MapBindingResult result,Model model) {

我在运行时遇到此错误

2020-10-14 12:22:36.157 ERROR 10426 --- [nio-8081-exec-2] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [/acscustompages] threw exception [Request processing Failed; nested exception is java.lang.IllegalStateException: argument type mismatch
Controller [com.xxx.pts.acscustompages.controller.DynamicFormController]
Method [public java.lang.String com.xxx.pts.acscustompages.controller.DynamicFormController.processForm(javax.servlet.http.HttpServletRequest,com.xxx.pts.acscustompages.model.form.DynamicFormBean,org.springframework.validation.MapBindingResult,org.springframework.ui.Model)] with argument values:
 [0] [type=org.apache.catalina.connector.RequestFacade] [value=org.apache.catalina.connector.RequestFacade@5c7f4f48],[1] [type=com.xxx.pts.acscustompages.model.form.DynamicFormBean] [value=DynamicFormBean(items={firstName=})],[2] [type=org.springframework.validation.BeanPropertyBindingResult] [value=org.springframework.validation.BeanPropertyBindingResult: 0 errors],[3] [type=org.springframework.validation.support.BindingAwareModelMap] [value={dynamicFormBean=DynamicFormBean(items={firstName=}),org.springframework.validation.BindingResult.dynamicFormBean=org.springframework.validation.BeanPropertyBindingResult: 0 errors}] ] with root cause

java.lang.IllegalArgumentException: argument type mismatch
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]

所以我尝试在代码中实例化一个MapBindingResult对象:

        result = new MapBindingResult(formFields,"items");
        if (StringUtils.isEmpty(fieldValue)) {
            result.rejectValue(fieldName,MessageCodes.FIELD_TOO_LONG);
        }
        if (result.hasErrors()) {

这使我能够捕获新BindingResult实例中的错误,但是错误消息未显示在表单上。仔细研究其原因,我意识到我的新result值未添加model中,而是包含一个错误BeanPropertyBindingResult的实例,如链接中所示调试器屏幕截图(我无法发布嵌入式图像):

https://i.stack.imgur.com/KtRqc.png

我有两个问题:

  1. 如何设置此控制器的Model,使其使用MapBindingResult而不是BeanPropertyBindingResult来捕获验证码所产生的错误

  2. 如何以Thymeleaf形式引用特定于字段的错误消息?这是我计划使用的:

<span th:if="#{fields.hasErrors(__${items['firstName']}__}" th:error="*{items['firstName']}">First name error</span>

但是我不确定这是否行得通。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?