Spring Boot 2.4.2 and Thymeleaf 3.0.12 - access static methods - spring-boot

Since I switched to Spring Boot 2.4.2 my Thymeleaf templates are broken. When I want to access a static member in Spring Controller I get the following error:
Exception processing template "template_name": Instantiation of new
objects and access to static classes is forbidden in this context.
The code looks like:
th:text="${T(com.test).testMethod("1234")}"
Do you have any recommendation to fix this?

This change is the part of Thymeleaf 3.0.12. They improve restricted expression evaluation mode security by restriction of the access to static code (#identifier# in OGNL, T(identifier) in SpringEL). What they have done by themselves? ... "Avoided instantiation of new objects and calls to static classes" as stated in release notes. You may move the JAVA calls into your controller and put the result into the view model. After just access this variable from Thymeleaf template.

Another quick fix is the use of th:with
th:text="${testText}"
th:with="testText=${T(com.test).testMethod("1234")}"
Source/Kudos: https://github.com/thymeleaf/thymeleaf/issues/816#issuecomment-791921248 and https://github.com/thymeleaf/thymeleaf/issues/816#issuecomment-826401631

There is a workaround to use method from beans registered at the Spring Application Context with the #beanName syntax. Like this:
<div th:text="${#testService.testMethod('123')}">...</div>
http://www.thymeleaf.org/doc/articles/springmvcaccessdata.html

Related

getRequestURI is null with Netty and Spring Boot 3

In Thymeleaf < 3.1 I used below expression to get the request URI.
th:classappend="${#arrays.contains(urls, #httpServletRequest.getRequestURI()) ? 'active' : ''}"
It worked all the time till recently I upgraded to Spring Boot 3.0 that pulls Thymeleaf 3.1. I am getting this exceptions:
[THYMELEAF][parallel-2] Exception processing template "index": Exception evaluating SpringEL expression: "#arrays.contains(urls, #servletServerHttpRequest.getRequestURI()) ? 'active' : ''" (template: "fragments/header" - line 185, col 6)
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1011E: Method call: Attempted to call method getRequestURI() on null context object
What is the alternative now since I am using Netty instead of Tomcat in Spring Boot 3.0? I could not figure this from here.
As a workaround, for now to tackle this, I am using:
#GetMapping ("/")
String homePage(Model model) {
model.addAttribute("pagename", "home");
return "index";
}
AND
th:classappend="${pagename == 'home' ? 'active' : ''}"
In Thymeleaf 3.0, access is provided to HttpServletRequest:
#request : direct access to the javax.servlet.http.HttpServletRequest object associated with the current request. reference
This has been removed from Thymeleaf in 3.1.0. Here is the equivalent section from the documentation: Web context namespaces for request/session attributes, etc..
The "what's new in 3.1" documentation does not specifically mention HttpServletRequest, but it does mention the removal of all the "web-API based expression utility objects".
The #request, #response, #session, and #servletContext are no longer available to expressions in Thymeleaf 3.1.
Spring Boot 3.0.0 uses Thymeleaf 3.1.0 (as you noted).
What to do instead?
See the related GitHub issue: Recommended way to go after upgrade to SpringBoot3 - attributes
Specifically:
These objects are not directly available in templates in Thymeleaf 3.1 for security reasons. The recommended way to make this information available to templates is to add the specific pieces of information that are really needed by the template as context variables (model attributes in Spring).
Example:
model.addAttribute("servletPath", request.getServletPath();
That is the same basic approach as what you are already doing, in your work-around.
See also: Remove web-API based expression utility objects
Adding to #andrewJames answer,
If you are using request.getServletPath() in many pages, then in such case, it's more convenient to use Spring's #ModelAttribute annotation in a #ControllerAdvice class. It will register this #ModelAttribute method for all controllers in your app. Example:
#ControllerAdvice
public class GlobalController {
#ModelAttribute("servletPath")
String getRequestServletPath(HttpServletRequest request) {
return request.getServletPath();
}
}
Finally in any page you can access by using:
${servletPath}

What is exactly server.error.path property?

In Spring Boot, what is the purpose of server.error.path property in application.properties file?
The documentation just says:
Path of the error controller
But I want a clear description of this property with an example.
server.error.path - used as part of url for error pages.
site.getBaseUrl() + "/error"
For example some error happen on server side and you decide redirect user to error page like this:
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/images/custom-error-page-aws-404-example.png
Code example of error controller you can find here:
https://www.logicbig.com/tutorials/spring-framework/spring-boot/implementing-error-controller.html
You can use this property in #RequestMapping("/error"). But instead of "/error" you can use "${server.error.path}"
UPDATE:
Also, Spring Boot BasicErrorController use server.error.path property
Property server.error.path in spring boot application used to define an error path while dealing with custom error handler. In Spring we create custom error handler using functional interface ErrorController, ths interface has a String type method getErrorPath which helps us to return the error page path(our error page as view).
But from Spring 2.3.0 this getErrorPath() method has been deprecated and replaced with server.error.path to manage the error path.
e.g. server.error.path=/error
For more detail about interface ErrorController, please refer Spring doc for ErrorController

What is the right approach to process a Freemarker Template on the fly in Spring service bean?

I a few existing freemarker templates (ftl) in Spring MVC project. They are using standard spring tags (#spring.url, #spring.message and #spring.bind). While using standard approach of using FreemarkerConfig inside the servlet context (with ViewResolver) all of the templates work fine.
I also have some email templates which is programatically being created, but they are not using any spring tags or additional tlds.
The twist is, I have a task to use the regular template and process them using Freemarker Template class from a Service Bean. The service class have the find the template in the specified location ("/WEB-INF/views"), but it does not process the spring tags, and finds "spring" variable to null. I have set "auto_import" to "/spring.ftl as spring", but my understanding is it will only work within Web/servlet context? Am I using the right approach here? What is the best way to process a "Template" outside and how to expose the spring.tld or anyother.ltd for it? Appreciate your help / pointers!

how to implement spring support for custom template engine?

I've decided to use custom template engine with Spring MVC framework.
my templates implemented in java and have method for rendering into String:
public String render(Map context);
how to configure spring to make them available in Controller beans as views, for example like:
ModelAndView modelAndView = new ModelAndView("activationPage"); // - view name which will actually be java class name reference.
modelAndView.addObject("validationResult", validationResult);
return modelAndView;
Model will be passed as context in code connecting spring and my template engine.
You need to implement org.springframework.web.servlet.View (which should be easy, you already have something very similar to the render method it needs), as well as org.springframework.web.servlet.ViewResolver, which maps the view names (e.g. "activationPage") on your custom views.
Once you have that, drop a bean of your ViewResolver class into the context, and (unless you've done something else that gets in the way) it should automatically be picked up by Spring and should just work. if you have other ViewResolvers already in there, they may get into a fight over who gets to resolve the view, in which case ask a new question.
Hi I am the author of Rythm template engine, about half year ago I am having the same requirement like you. What I did is to read the source code of Velocity and Freemarker view of SpringFramework. And then create the Rythm view for spring following their approach.
It's easy to follow something that is already there, and it makes your implementation in good quality to follow the official module. Good luck on you :-)

Freemarker Servlet and Struts : access java methods and variables in template

I want to access the Session, Application and Request variables of a Struts web application.
It tells me Session is of type 'HttpSessionHashModel', and looking up the API I saw this class has a method 'isEmpty()'. so I tried for example
<#assign a = Session.isEmpty()>
but it tells me there is no such element 'Session.isEmpty'.
does anyone have an Idea why it is not working?
In Session there are several classes which I could access via
<#assign b = Session["classname"]> (it tells me b is then of type freemarker.ext.beans.StringModel), but I can neither call the methods of class StringModel nor can I get to the objects and methods stored in 'classname'.
hope someone can help
Use the #s macro:
Tags distributed with Struts are automatically made available to FreeMarker templates. To use any tag add "#s." in front of the tag name. Like:
<#s.if test="printName">
<#s.property value="myBeanProperty" />
</#s.if>
References
Using Freemarker Templates - Using Struts tags on FreeMarker templates

Resources