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

如何在HTML的script标签CDATA中调用Thymeleaf自定义方言?

在Thymeleaf的CDATA块中使用自定义属性处理器生成URL的问题解决

问题描述

自定义了QuackUrlAttributeTagProcessor属性处理器和XContDialect方言,用于将Quack对象转换为完整URL。该处理器在普通HTML标签(如<link>)中可正常生成href属性,但在<script>标签的CDATA块内尝试通过[[xc:quackToHref="${quack}"]]调用时,抛出解析异常:

Caused by: org.attoparser.ParseException: Could not parse as expression: "xc:quackToHref="${quack}" (template: "articlequack" - line 38, col 22)

原因分析

自定义的QuackUrlAttributeTagProcessor是属性处理器,仅能处理HTML标签的属性节点,无法直接作用于文本内容(包括CDATA块中的文本)。Thymeleaf的[[...]]是文本表达式语法,会将内部内容当作表达式解析,而xc:quackToHref并非合法的Thymeleaf表达式,因此触发解析错误。

解决方案

方案一:将URL生成逻辑封装为表达式工具类(推荐)

把URL生成逻辑抽离为可在Thymeleaf表达式中直接调用的工具类,同时保留原属性处理器兼容旧用法:

  1. 创建URL生成工具类
@Component
public class QuackUrlUtility {
    @Autowired
    private ApplicationPropertyAccess applicationProperties;

    public String buildQuackUrl(Quack quack) {
        String url = QuackManager.buildUrlPath(quack);
        return applicationProperties.getFrontendBaseUrl() + url;
    }
}
  1. 修改自定义方言,注册表达式对象
    让XContDialect实现IExpressionObjectDialect接口,将工具类注册为Thymeleaf可用的表达式对象:
import org.thymeleaf.context.IExpressionContext;
import org.thymeleaf.expression.AbstractExpressionObjectFactory;
import org.thymeleaf.IEngineConfiguration;
import org.thymeleaf.dialect.IExpressionObjectDialect;
import org.thymeleaf.expression.IExpressionObjectFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class XContDialect extends AbstractProcessorDialect implements IExpressionObjectDialect, ApplicationContextAware {
    private ApplicationContext applicationContext;
    private QuackUrlUtility quackUrlUtility;

    public XContDialect() {
        super("XConsult Dialect", "xc", 1000);
    }

    @Override
    public Set<IProcessor> getProcessors(final String dialectPrefix) {
        final Set<IProcessor> processors = new HashSet<>();
        // 保留原属性处理器,兼容现有用法
        QuackUrlAttributeTagProcessor processor = new QuackUrlAttributeTagProcessor(dialectPrefix);
        AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
        factory.autowireBean(processor);
        processors.add(processor);
        return processors;
    }

    @Override
    public IExpressionObjectFactory getExpressionObjectFactory() {
        return new AbstractExpressionObjectFactory() {
            @Override
            public Set<String> getAllExpressionObjectNames() {
                return Collections.singleton("quackUrl");
            }

            @Override
            public Object buildObject(IExpressionContext context, String expressionObjectName) {
                if ("quackUrl".equals(expressionObjectName)) {
                    return quackUrlUtility;
                }
                return null;
            }

            @Override
            public boolean isCacheable(String expressionObjectName) {
                return true;
            }
        };
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        this.quackUrlUtility = applicationContext.getBean(QuackUrlUtility.class);
    }
}
  1. 在CDATA块中调用表达式工具
    直接在文本表达式中调用工具类的方法生成URL:
<script type="application/ld+json">
/*<![CDATA[*/
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Article",
      "url": "[[${xc:quackUrl.buildQuackUrl(quack)}]]",
      "isPartOf": { "@id": "test" },
      "author": {
        "name": "[[${article.nickname}]]",
        "@id": "https://example.de/profile/[[${article.authorId}]]"
      }          
    }
  ]
}
/*]]>*/
</script>

方案二:通过隐藏标签中转URL(客户端依赖)

如果不想修改处理器结构,可以先在页面隐藏标签中生成URL,再在CDATA块中引用该值:

  1. 添加隐藏标签生成URL
<link id="quackCanonicalUrl" rel="canonical" xc:quackToHref="${quack}" style="display: none;" />
  1. 在CDATA块中获取标签属性值
<script type="application/ld+json">
/*<![CDATA[*/
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Article",
      "url": "[[${document.getElementById('quackCanonicalUrl').href}]]",
      "isPartOf": { "@id": "test" },
      "author": {
        "name": "[[${article.nickname}]]",
        "@id": "https://example.de/profile/[[${article.authorId}]]"
      }          
    }
  ]
}
/*]]>*/
</script>

该方案依赖客户端DOM解析,不如服务端生成可靠,仅作为临时兼容方案。

总结

方案一通过扩展Thymeleaf表达式能力,实现了服务端直接生成URL,既兼容原有属性处理器用法,又能在CDATA等文本场景中使用,是更稳定、规范的实现方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 12:35:24