如何解决问题以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
我有两个问题:
-
如何设置此控制器的
Model
,使其使用MapBindingResult
而不是BeanPropertyBindingResult
来捕获验证码所产生的错误? -
如何以Thymeleaf形式引用特定于字段的错误消息?这是我计划使用的:
<span th:if="#{fields.hasErrors(__${items['firstName']}__}" th:error="*{items['firstName']}">First name error</span>
但是我不确定这是否行得通。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。