问题背景
在开发 Halo 博客主题时,如果需要同时支持 Halo Pro 和 Halo 开源版,会遇到一个棘手的问题:Pro 版特有的属性在开源版中不存在,直接访问会导致 500 错误。
错误示例
<!-- ❌ 这段代码在开源版会报错 -->
<th:block th:if="${globalInfo.captchaProvider == 'ALTCHA'}">
...
</th:block>
报错信息:
TemplateProcessingException: Property or field 'captchaProvider'
cannot be found on object of type 'run.halo.app.infra.actuator.GlobalInfo'
问题分析
GlobalInfo 的差异
| 属性 | 开源版 | Pro 版 |
|---|---|---|
| externalUrl | ✅ | ✅ |
| allowRegistration | ✅ | ✅ |
| siteTitle | ✅ | ✅ |
| captchaProvider | ❌ | ✅ |
| activations | ❌ | ✅ |
| brand | ❌ | ✅ |
为什么不能用反射检测?
<!-- ❌ 被 Thymeleaf 安全机制禁止 -->
<th:block th:if="${T(org.springframework.util.ReflectionUtils).findMethod(...)}">
报错: Access is forbidden for type 'org.springframework.util.ReflectionUtils'
为什么 ?. 不起作用?
SpEL 的 ?. 安全导航操作符只能处理 null 值,不能处理 属性不存在 的情况。
解决方案:使用 toString() 检测
<!-- ✅ 正确的兼容写法 -->
<th:block th:if="${globalInfo != null and #strings.contains(globalInfo.toString(), 'captchaProvider')}">
<th:block th:if="${#strings.equalsIgnoreCase(globalInfo.captchaProvider, 'ALTCHA')}">
<script src="/js/altcha-lib.iife.js"></script>
</th:block>
</th:block>
工作原理
- 第一层:
#strings.contains(globalInfo.toString(), 'captchaProvider')→ 判断是否是 Pro 版 - 第二层:安全访问属性值 → 只有 Pro 版才会执行
总结
| 方案 | 可行性 |
|---|---|
| 直接访问属性 | ❌ |
| 使用反射检测 | ❌ |
使用 ?. 操作符 |
❌ |
| 使用 toString() 检测 | ✅ |
通过这种方式,主题可以自动检测版本,无需用户手动配置!