spring-projects / spring-framework

Spring Framework
https://spring.io/projects/spring-framework
Apache License 2.0
55.77k stars 37.81k forks source link

Restore Freemarker support now that it supports Jakarta #30186

Closed anabright closed 6 days ago

anabright commented 1 year ago

I just upgraded my app to Spring Boot 3.0.5 (and consequently Spring 6.0.7) and there seems to be something broken with spring-boot-starter-freemarker.

In my Freemarker template, I have this line that used to work before the upgrade:

<div>${RequestParameters.myParam!}</div>

After the upgrade, I get this error when loading the template:

freemarker.core.InvalidReferenceException: The following has evaluated to null or missing:
==> RequestParameters  [in template "template.ftlh" at line 136, column 11]

----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----

----
FTL stack trace ("~" means nesting-related):
    - Failed at: ${RequestParameters.myParam!}  [in template "template.ftlh" at line 136, column 9]

It seems like RequestParameters is not available anymore.

I originally posted this issue in the spring-boot repo and @wilkinsona kindly pointed out that due to https://github.com/spring-projects/spring-framework/commit/d84ca2ba90d27a7c63d7b35a6259b5b9cf341118, there's no longer a RequestParameters entry in the model.

Are there plans to fix this?

Thanks!

damir78 commented 1 year ago

Hello @anabright , Thank you. I created an issue: #34637 but unfortunately it was closed. The implementation of "RequestParameters" is https://github.com/apache/freemarker/blob/v2.3.32/src/main/java/freemarker/ext/servlet/HttpRequestHashModel.java and uses javax. (for example javax.servlet) instead jakarta. ( for example jakarta.servlet).

Paul-Gerarts commented 1 year ago

yes, I upgraded to SpringBoot 3.0.2 as parent and Freemarker had a hash for RequestParameters which doesn't exist anymore.

From the Freemarker docs: "FreemarkerServlet also puts 3 hashes into the data-model, by which you can access the attributes of the 3 objects directly. The hash variables are: Request, Session, Application (corresponds to ServletContext). It also exposes another hash named RequestParameters that provides access to the parameters of the HTTP request."

Those RequestParameters could be accessed directly in your template, for example: <input name="myParam" type="hidden" value="${RequestParameters.myParam!}"/>

As I'm working in a legacy project from waaaay back, I was hoping to find a more elegant way rather than getting the parameter from the servletRequest and adding it as an attribute to my model.

mrhcongc commented 1 year ago

I see that the issue is more than just not supporting the jakarta.* The code in the FreeMarkerView.buildTemplateModel has been changed and has removed all the model additions. I created my own implementations of a few classes to allow me to add the RequestParameters and Request back into the model. If fixed in a future release, I'll just remove my classes.

startjava commented 10 months ago

use spring-boot 3.1.2

use url: http://localhost:8080/test20?selectedValue=d

template code: <#if listString?? && listString?size!=0> <#list listString as list> <#if RequestParameters['selectedValue']==list> ${list} </#if> <#if RequestParameters['selectedValue']!=list> ${list} </#if> </#list> </#if>

controller code: @RequestMapping("test20") public String test20(Model model,HttpServletRequest request,HttpServletResponse response)

{ List listString=new ArrayList(); listString.add("a"); listString.add("b"); listString.add("c"); listString.add("d"); listString.add("e"); request.setAttribute("listString",listString); return "test20"; }

run result : freemarker.core.InvalidReferenceException: The following has evaluated to null or missing: ==> RequestParameters [in template "test20.ftlh" at line 12, column 12]

Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??

FTL stack trace ("~" means nesting-related):

[in template "test20.ftlh" at line 12, column 7]

same question!


@anabright @mrhcongc @damir78 @Paul-Gerarts

startjava commented 10 months ago

https://github.com/spring-projects/spring-boot/issues/34637

startjava commented 10 months ago

@wilkinsona

snicoll commented 7 months ago

@anabright unfortunately, there's nothing we can do here. Even the latest version of Freemarker does not use Jakarta as far as I can tell.

If you need to use the Servlet integration of Freemarker, only a change in freemarker can help here.

doctore commented 2 months ago

I had this problem upgrading an old project from Spring 3 to Spring 6, by now we have no time to replace the technology used to render the views, so my solution was adding it as a new model attribute of FreeMarker.

Replacing:

<#if RequestParameters['authfail']??>
  ...
</#if>

By, in backend:

@ModelAttribute("rawRequestParameters") 
public String getRequestParameters(HttpServletRequest request) {
  return ofNullable(request)
            .map(HttpServletRequest::getQueryString)
            .orElse("");
}

and in frontend:

<#if rawRequestParameters?? && rawRequestParameters == "authfail">
  ...
</#if>
anabright commented 2 months ago

@anabright unfortunately, there's nothing we can do here. Even the latest version of Freemarker does not use Jakarta as far as I can tell.

If you need to use the Servlet integration of Freemarker, only a change in freemarker can help here.

There's supposed to be a Freemarker release soon that supports jakarta. @snicoll maybe when it's out Spring can be updated to use the new version and add back the missing model attributes like RequestParameters?

snicoll commented 2 months ago

@anabright it's not really actionable right now so please ping again when it's actually available and we'll have a look.

FloTrEu commented 3 weeks ago

@snicoll According to https://github.com/apache/freemarker/pull/94#issuecomment-2167603161

You have to use freemarker.ext.jakarta.servlet package instead of freemarker.ext.servlet package, and freemarker.ext.jakarta.jsp instead of freemarker.ext.jsp, everywhere in your application. It's not enough to just update your FreeMarker dependency. We support both pre-Jakarata and Jakarta environments in the same artifact, so the name of the non-Jakarta classes did not change.

I've checked version 2.3.33 and it includes freemarker.ext.jakarta.servlet package. Could you please give it a try?

bclozel commented 3 weeks ago

It looks like Freemarker 2.3.33 was released in May 2024. Spring Framework 6 was released in November 2022.

We could reintroduce this support in Spring Framework 6.2 at the earliest, so two entire minor versions into the 6th generation. Because we didn't get a lot of requests for this in the meantime, I'm reopening this enhancement request and adding it to our backlog. We'll prioritize this according to community demand.

snicoll commented 6 days ago

@anabright and others interested by this, this should be available shortly in 6.2.0-SNAPSHOT and in the next milestone coming in August. Please give that a try and let us know if you experience a problem.

anabright commented 6 days ago

@snicoll thanks for the nudge, unfortunately we migrated our freemarker templates to thymeleaf so we could upgrade our project to Spring Boot 3 and it's not in our plans to revert the changes.