尝试访问 Thymeleaf 表单中的验证错误时出错

Posted

技术标签:

【中文标题】尝试访问 Thymeleaf 表单中的验证错误时出错【英文标题】:Error while trying to access the validation error in Thymleaf form 【发布时间】:2020-04-24 19:34:10 【问题描述】:

属性、setter 和 getter 的命名是否有任何规则? 我有一个模型类,它具有名为 uuidString 属性和 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 中的表单字段时出现错误消息的问题

尝试在 Thymeleaf 和 Spring Security 项目中插入图像时出现 404 错误

表单验证失败时出现 502 Bad Gateway 错误