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

Micronaut集成Thymeleaf模板时#request对象为空问题咨询

Micronaut集成Thymeleaf时#request等内置web对象为null的问题解决

问题背景

我正在开发一个使用Micronaut 2.57、Thymeleaf 3.0.12、micronaut-views-thymeleaf 2.21的应用,用于渲染视图。根据Thymeleaf官方文档说明,模板中应该可以直接使用#request对象,但实际使用时该对象始终为null。


复现代码

模板代码

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Home</title>
</head>
<body>
<h3>#request.contextPath</h3>
<span th:utext="${#request.contextPath}"></span>

<h3>#request.requestURI</h3>
<span th:utext="${#request.requestURI}"></span>

<h3>#request.requestURL</h3>
<span th:utext="${#request.requestURL}"></span>
</body>
</html>

控制器代码

import io.micronaut.http.HttpResponse;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.views.View;

@Controller
public class HomeController {

    @View("home2")
    @Get("/")
    public HttpResponse<Object> showHome() {
        return HttpResponse.ok();
    }
}

报错信息

启动应用后访问localhost:8080,出现如下堆栈报错,提示#request对象不存在:

17:52:57.326 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 2231ms. Server Running: http://localhost:8080
17:53:06.644 [io-executor-thread-1] ERROR org.thymeleaf.TemplateEngine - [THYMELEAF][io-executor-thread-1] Exception processing template "home2": Exception evaluating OGNL expression: "#request.contextPath" (template: "home2" - line 8, col 7)
org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating OGNL expression: "#request.contextPath" (template: "home2" - line 8, col 7)
    at org.thymeleaf.standard.expression.OGNLVariableExpressionEvaluator.evaluate(OGNLVariableExpressionEvaluator.java:191)
    at org.thymeleaf.standard.expression.OGNLVariableExpressionEvaluator.evaluate(OGNLVariableExpressionEvaluator.java:95)
    at org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression(VariableExpression.java:166)
    at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:66)
    at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109)
    at org.thymeleaf.standard.expression.Expression.execute(Expression.java:138)
    at org.thymeleaf.standard.processor.StandardUtextTagProcessor.doProcess(StandardUtextTagProcessor.java:87)
    at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74)
    at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95)
    at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633)
    at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314)
    at org.thymeleaf.engine.OpenElementTag.beHandled(OpenElementTag.java:205)
    at org.thymeleaf.engine.TemplateModel.process(TemplateModel.java:136)
    at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:661)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1067)
    at io.micronaut.views.thymeleaf.ThymeleafViewsRenderer.render(ThymeleafViewsRenderer.java:109)
    at io.micronaut.views.thymeleaf.ThymeleafViewsRenderer.lambda$render$1(ThymeleafViewsRenderer.java:96)
    at io.micronaut.core.io.Writable.writeTo(Writable.java:77)
    at io.micronaut.http.server.netty.RoutingInBoundHandler.lambda$encodeHttpResponse$7(RoutingInBoundHandler.java:1683)
    at io.micronaut.scheduling.instrument.InvocationInstrumenterWrappedRunnable.run(InvocationInstrumenterWrappedRunnable.java:47)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: ognl.OgnlException: source is null for getProperty(null, "contextPath")
    at ognl.OgnlRuntime.getProperty(OgnlRuntime.java:3229)
    at ognl.ASTProperty.getValueBody(ASTProperty.java:114)
    at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
    at ognl.SimpleNode.getValue(SimpleNode.java:258)
    at ognl.ASTChain.getValueBody(ASTChain.java:141)
    at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
    at ognl.SimpleNode.getValue(SimpleNode.java:258)
    at ognl.Ognl.getValue(Ognl.java:537)
    at ognl.Ognl.getValue(Ognl.java:501)
    at org.thymeleaf.standard.expression.OGNLVariableExpressionEvaluator.executeExpression(OGNLVariableExpressionEvaluator.java:328)
    at org.thymeleaf.standard.expression.OGNLVariableExpressionEvaluator.evaluate(OGNLVariableExpressionEvaluator.java:170)
    ... 23 common frames omitted

其他内置变量测试结果

  • #locale - 正常可用
  • #ctx - 正常可用
  • #request - 始终为null
  • #response - 始终为null
  • #session - 始终为null
  • #servletContext - 始终为null

排查结论

已知io.micronaut.views.thymeleaf.ThymeleafViewsRenderer会给渲染引擎传递WebContext,调试发现contextVariableMap中包含的key列表为[ctx, root, vars, object, locale, request, response, session, servletContext, conversions, uris, calendars, dates, bools, numbers, objects, strings, arrays, lists, sets, maps, aggregates, messages, ids, execInfo, httpServletRequest, httpSession],但其中的servletContext、httpServletRequest、request取值均为null。

进一步排查org.thymeleaf.standard.expression.StandardExpressionObjectFactory类的逻辑:

if (REQUEST_EXPRESSION_OBJECT_NAME.equals(expressionObjectName)) {
    if (context instanceof IWebContext) {
        return ((IWebContext) context).getRequest();
    }
    return null;
}

发现传入的上下文类型为WebEngineContext,但instanceof判断IWebContext结果为false。最终定位到根本原因:Micronaut中创建的WebEngineContext属于io.micronaut.views.thymeleaf.WebEngineContext类,继承自EngineContext,和Thymeleaf原生实现了IWebContext接口的org.thymeleaf.context.WebEngineContext不是同一个类,因此无法被Thymeleaf的内置对象工厂识别,无法正确注入request等对象。


解决方案

方案1:升级依赖版本

该问题属于Micronaut Thymeleaf集成模块的历史缺陷,在micronaut-views-thymeleaf 3.0及以上版本中已经修复,将Micronaut框架及对应视图模块升级到3.x最新稳定版即可原生支持#request等内置对象。

方案2:适配内置对象调用方式

Micronaut自定义的WebEngineContext本身提供了getRequest公共方法,无需额外配置,直接在模板中使用${#ctx.getRequest()}即可获取request对象,对应属性可以通过${#ctx.getRequest().getContextPath()}的方式读取。

方案3:手动注入request属性

临时解决方案,可以在控制器中注入HttpRequest对象,放入返回的模型数据中,模板中直接读取对应属性即可:

import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.views.View;
import java.util.Map;

@Controller
public class HomeController {

    @View("home2")
    @Get("/")
    public HttpResponse<Map<String, Object>> showHome(HttpRequest<?> request) {
        return HttpResponse.ok(Map.of(
            "contextPath", request.getContextPath(),
            "requestURI", request.getUri(),
            "requestURL", request.getServerAddress() + request.getUri()
        ));
    }
}

模板中直接用${contextPath}等对应key取值即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.12 15:51:01