Micronaut - Thymeleaf - #request is null

Viewed 224

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.

1 Answers

It turns out that there is a incompatibility with Micronaut and the Thymeleaf Servlet objects. I've reported the issue https://github.com/micronaut-projects/micronaut-views/issues/241 and we'll see from there.

At the moment you can use #ctx.getRequest() to get the instance of the io.micronaut.http.server.netty.NettyHttpRequest.

The other objects are still a mystery for me too.

Related