You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Thymeleaf th:replace使用动态变量路径时模板解析报错问题

问题原因

两个核心问题导致异常和功能失效:

  1. 枚举类的私有字段没有提供public getter方法,Thymeleaf的OGNL表达式无法读取私有属性值,这是抛出表达式解析异常的直接原因。另外枚举前两个模板路径写了多余的单引号,路径本身非法:"templates/firstPageInclude'.html'"、"templates/summaryInclude'.html'"。
  2. Thymeleaf的th:replace默认不会直接把${}表达式的返回值识别为模板路径,直接写th:replace="${summaryItem.pagePath}"时,引擎会把表达式结果当成普通变量输出,不会去加载对应的模板文件。同时你写的id="${summaryItem.anchor}"缺少th:前缀,动态表达式根本不会被解析,会原样输出字符串。
修复步骤
  1. 修正枚举错误,补全必要方法
    先删掉前两个枚举值路径里多余的单引号,保证路径和实际文件位置一致,再给所有私有字段添加public getter方法:
    public enum FollowupPdfSummaryEnum {
       COVER("coverAnchor", "templates/firstPageInclude.html", "Couverture", FollowupBlocEnum.COVER_PAGE),
       SUMMARY("summaryAnchor", "templates/summaryInclude.html", "Sommaire"),
       SITUATION_PLAN("situationPlanAnchor", "templates/situationPlanInclude.html", "Plan de situation", FollowupBlocEnum.SITUATION_PLAN),
       BUILDING_VISUAL("buildingVisualAnchor", "templates/visualInclude.html", "Visuels", FollowupBlocEnum.BUILDING_VISUAL),
       HEALTH_BOOK("healthBookAnchor", "templates/healthBookBatInclude.html", "Carnet de santé", FollowupBlocEnum.HEALTH_BOOK),
       IDENTITY_CARD("identityCardAnchor", "templates/identityCardInclude.html", "Fiche d'identité", FollowupBlocEnum.IDENTITY_CARD),
       COUNTER_SITUATION_PLAN("counterSituationAnchor", "templates/counterSituationPlanInclude.html", "Plan des compteurs", FollowupBlocEnum.COUNTER_SITUATION_PLAN),
       DANGEROUS_MATERIAL("dangerousMaterialAnchor", "templates/dangerousMaterialInclude.html", "Diagnostic Matières Dangereuses", FollowupBlocEnum.DANGEROUS_MATERIAL_DIAGNOSIS),
       RECAP_WORK_TODO("workDoneAnchor", "templates/workTodoListInclude.html", "Récapitulatif des travaux à réaliser", FollowupBlocEnum.WORK_DONE_REPORT),
       VISIT_REPORT("visitReportAnchor", "templates/visitReportInclude.html", "Rapport de visite", FollowupBlocEnum.VISIT_REPORT),
       VISIT_REPORT_OCCUPATION("visitReportOccupationAnchor", "templates/occupationVisitReportInclude.html", "Rapport de visite : Occupations", FollowupBlocEnum.OCCUPATION_VISIT_REPORT),
       ANNEXE("annexeAnchor", "templates/annexeInclude.html", "Annexes");
       
       private final String anchor;
       private final String pagePath;
       private final String title;
       private final FollowupBlocEnum block;
    
       private FollowupPdfSummaryEnum(String anchor, String pagePath, String title, 
       FollowupBlocEnum block) {
          this.anchor = anchor;
          this.pagePath = pagePath;
          this.title = title;
          this.block = block;
       }
       private FollowupPdfSummaryEnum(String anchor, String pagePath, String title) {
          this.anchor = anchor;
          this.pagePath = pagePath;
          this.title = title;
          this.block = null;
       }
    
       // 补全getter方法
       public String getAnchor() {return anchor;}
       public String getPagePath() {return pagePath;}
       public String getTitle() {return title;}
       public FollowupBlocEnum getBlock() {return block;}
    }
    
  2. 修改模板代码,使用正确的Thymeleaf语法
    动态模板路径需要用__${}__预处理语法:该语法会先执行内部表达式,把返回值拼接到属性位置,再让引擎按模板路径加载文件。所有绑定动态值的属性都要加th:前缀,遍历Model传参时优先用${}取值,修改后的代码如下:
    <th:block th:each="summaryItem : ${summary}">
        <page-after th:id="${summaryItem.anchor}">
            <div th:replace="__${summaryItem.pagePath}__"></div>
        </page-after>
    </th:block>
    
注意事项
  • 所有被引入的子模板不要包含完整的<html>、<body>根标签,只保留需要渲染的内容片段即可,避免OpenHtmlToPdf解析时出现标签嵌套错误。
  • 如果需要给子模板传递参数,可以在路径后按标准th:replace传参格式追加,例如th:replace="__${summaryItem.pagePath}__ (currentItem=${summaryItem})"。
  • 确认模板文件实际存放路径和枚举配置的路径完全匹配,Spring Boot项目默认模板根路径为src/main/resources/templates/,枚举中写的相对路径要和该根路径对应,同时注意路径大小写敏感。

内容的提问来源于stack exchange,提问作者Gauthier

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 18:36:20