尝试访问 Thymeleaf 表单中的验证错误时出错
Posted
技术标签:
【中文标题】尝试访问 Thymeleaf 表单中的验证错误时出错【英文标题】:Error while trying to access the validation error in Thymleaf form 【发布时间】:2020-04-24 19:34:10 【问题描述】:属性、setter 和 getter 的命名是否有任何规则?
我有一个模型类,它具有名为 uuid
的 String
属性和 setter 和 getter setUUID(String uuid)
getUUID()
但是当尝试使用 Thymleaf 验证输入以显示错误(如果存在)时,它会崩溃注意:deviceName
属性工作正常
以下是我的 sn-ps 代码:
模型
@Entity
public class Device
@Id
@NotBlank
private String uuid;
@NotNull
@NotBlank
private String deviceName;
public String getUUID()
return uuid;
public void setUUID(String uuid)
this.uuid = uuid;
public String getDeviceName()
return deviceName;
public void setDeviceName(String deviceName)
this.deviceName = deviceName;
表格
<form method="post" action="#" th:action="@/devices/save" th:object="$device">
<input th:field="*UUID" th:classappend="$#fields.hasErrors('*UUID') ?'border-danger'" type="text" name="uuid">
<input th:field="*deviceName" th:classappend="$#fields.hasErrors('deviceName') ?'border-danger'" type="text" name="deviceName">
</form>
尝试解析模板时出错 模板解析时出错(模板:“类路径资源 [templates/Devices/add-device.html]”) org.thymeleaf.exceptions.TemplateInputException:模板解析时出错(模板:“类路径资源[templates/Devices/add-device.html]”) 在 org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:241) 在 org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parseStandalone(AbstractMarkupTemplateParser.java:100) 在 org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:666) 在 org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098) 在 org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1072) 在 org.thymeleaf.spring5.view.ThymeleafView.renderFragment(ThymeleafView.java:362) 在 org.thymeleaf.spring5.view.ThymeleafView.render(ThymeleafView.java:189) 在 org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1373) 在 org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1118) 在 org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1057) 在 org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) 在 org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) 在 org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:634) 在 org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:741) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320) 在 org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:126) 在 org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) 在 org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:118) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) 在 org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) 在 org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) 在 org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:158) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) 在 org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) 在 org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) 在 org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) 在 org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) 在 org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) 在 org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) 在 org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) 在 org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) 在 org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) 在 org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) 在 org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) 在 org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 java.lang.Thread.run(Thread.java:748) 原因:org.attoparser.ParseException:评估 SpringEL 表达式的异常:“#fields.hasErrors('uuid')”(模板:“Devices/add-device” - 第 28 行,第 87 列) 在 org.attoparser.MarkupParser.parseDocument(MarkupParser.java:393) 在 org.attoparser.MarkupParser.parse(MarkupParser.java:257) 在 org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230) ... 88 更多 引起:org.thymeleaf.exceptions.TemplateProcessingException:评估 SpringEL 表达式的异常:“#fields.hasErrors('uuid')”(模板:“Devices/add-device” - 第 28 行,第 87 列) 在 org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:290) 在 org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression(VariableExpression.java:166) 在 org.thymeleaf.standard.processor.AbstractStandardExpressionAttributeTagProcessor.doProcess(AbstractStandardExpressionAttributeTagProcessor.java:144) 在 org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74) 在 org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95) 在 org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633) 在 org.thymeleaf.engine.ProcessorTemplateHandler.handleStandaloneElement(ProcessorTemplateHandler.java:918) 在 org.thymeleaf.engine.TemplateHandlerAdapterMarkupHandler.handleStandaloneElementEnd(TemplateHandlerAdapterMarkupHandler.java:260) ... 90 更多 原因:org.springframework.beans.NotReadablePropertyException:bean 类 [com.logica.eguestbookservice.Models.Device] 的无效属性“uuid”:Bean 属性“uuid”不可读或具有无效的 getter 方法:返回类型是否getter 的参数类型是否匹配 setter 的参数类型? 在 org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyValue(AbstractNestablePropertyAccessor.java:622) 在 org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyValue(AbstractNestablePropertyAccessor.java:612) 在 org.springframework.validation.AbstractPropertyBindingResult.getActualFieldValue(AbstractPropertyBindingResult.java:104) 在 org.springframework.validation.AbstractBindingResult.getFieldValue(AbstractBindingResult.java:228) 在 org.springframework.web.servlet.support.BindStatus.(BindStatus.java:129) 在 org.springframework.web.servlet.support.RequestContext.getBindStatus(RequestContext.java:903) 在 org.thymeleaf.spring5.context.webmvc.SpringWebMvcThymeleafRequestContext.getBindStatus(SpringWebMvcThymeleafRequestContext.java:227) ... 113 更多
【问题讨论】:
错误/堆栈跟踪与您的代码不匹配。您的属性名为UUID
而不是 uuid
后者是字段的名称,而不是属性的名称。属性的名称是通过 getter/setter 来识别的。
这就是为什么我无法解决它,因为我不明白为什么它希望属性为uuid
它没有。如前所述,您的代码与错误不匹配。您应该使用UUID
,因为这是属性的名称。
模型中的代码有字符串uuid
,但setter函数是setUUID()
,这是问题所在,但感谢@Wim,我通过将setter名称更改为setUuid()
解决了这个问题
字段的命名方式无关紧要,getter/setter 的名称很重要,因为它是属性的名称。如果您有 setUuid
,则该字段可以命名为 foo
,该属性命名为 uuid
而不是 foo
。所以UUID
应该像从getter/setter 派生的那样工作。但如前所述,您以表格形式发布的内容与错误不符。属性的命名在 Java Bean 规范中定义(参见 oracle.com/technetwork/java/javase/documentation/…)。
【参考方案1】:
Thymeleaf 很可能遵循 JavaBean 规范,因此您需要将方法命名为 getUuid()
和 setUuid
并将形式更改为:
<input th:field="*uuid" th:classappend="$#fields.hasErrors('*uuid') ?'border-danger'" type="text" name="uuid">
【讨论】:
这样可以正常工作,但是如果我想像setUUID()
一样设置setter方法名称怎么办?
以及在哪里可以找到与这些命名模式相关的规范?
请参阅thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#variables,其中指出“在启用 Spring MVC 的应用程序中,OGNL 将被 SpringEL 替换,但其语法与 OGNL 非常相似(实际上,对于大多数常见情况完全相同)。 " docs.spring.io/spring/docs/current/spring-framework-reference/… 有更多关于 SpringEL 的信息。以上是关于尝试访问 Thymeleaf 表单中的验证错误时出错的主要内容,如果未能解决你的问题,请参考以下文章
尝试通过 AJAX 验证 Laravel 表单时出现错误 422
尝试使用 Thymeleaf 加载 HTML 文件时出现 Spring Boot 404
验证 express js 中的表单字段时出现错误消息的问题