I started working on a Micronaut 2.57 application with Thymeleaf 3.0.12 and micronaut-views-thymeleaf 2.21 that can present some views. According to the Thymeleaf docs, I should have the #request object but I think I always get null.
The thymeleaf template looks like:
<!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>
The controller to present the view:
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();
}
}
After starting the application and browsing to localhost:8080, I receive the following stacktrace which indicates that the #request does not exist
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
Does anyone know why this variable might not be passed to the template? Other variables that I've tested:
- #locale - works
- #ctx - works
- #request - always null
- #response - always null
- #session - always null
- #servletContext - always null
The io.micronaut.views.thymeleaf.ThymeleafViewsRenderer passes a WebContext to the engine it's rendering, so I'm clueless why these objects are not accessible.
Any pointers are appreciated.
EDIT: I've debugged somewhat further and this is the list of keys that the contextVariableMap contains: [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] but the servletContext,httpServletRequest, request are all null.
Second edit: looking even deeper I stumbled across
if (REQUEST_EXPRESSION_OBJECT_NAME.equals(expressionObjectName)) {
if (context instanceof IWebContext) {
return ((IWebContext) context).getRequest();
}
return null;
}
in the org.thymeleaf.standard.expression.StandardExpressionObjectFactory. The passed context is of type WebEngineContext, which should implement IWebContext but the instanceof evaluation is false...
Diving even deeper now.
Looking into the creation of the WebEngineContext I stumble on this class: o.micronaut.views.thymeleaf.WebEngineContext which is defined as public class WebEngineContext extends EngineContext. This is different from the org.thymeleaf.context.WebEngineContext that implements the interface to resolve #request.