Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error on thymeleaf th:field in TemplateEngine process [SPR-17603] #22135

Closed
spring-issuemaster opened this issue Dec 17, 2018 · 2 comments

Comments

@spring-issuemaster
Copy link
Collaborator

commented Dec 17, 2018

Tom opened SPR-17603 and commented

There was little response in StackOverflow but the nature of this problem leads me to believe that it is a bug and so i'm posting this here. I have looked up and down on the web without finding anything related specifically to this.

 

I am also setting the priority to Major as it is putting some major problems into our development.

 

I am currently porting an older web application to spring, The older web application had ajax requests where it would return some html code along with some status messages in json format.

 

Trying to reimplement this functionality leads to error messages in spring.

 Error occurs when processing thymeleaf template containing th:field using TermplateEngine process method.

 

Thymeleaf template

<form th:object="${customer}">    
<label>Name</label>
  <input type="text" th:field="*{name}" />
</form> 

 

Controller
// — Class member —
@Autowired
private TemplateEngine templateEngine;
// ----------

@ResponseBody
@GetMapping(value="/form1")
public String form1() throws JsonProcessingException

{     Customer customer = new Customer("Burger King");     Context templateContext = new Context(); templateContext.setVariable("customer", customer);     AjaxResponse response = new AjaxResponse(); response.html = templateEngine.process("form", templateContext); // <-- ERROR    

 

response.additionalData = "ab123"; return objectMapper.writeValueAsString(response); }

 

Error output
 org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/form.html]")

Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Cannot process attribute '{th:field,data-th-field}': no associated BindStatus could be found for the intended form binding operations. This can be due to the lack of a proper management of the Spring RequestContext, which is usually done through the ThymeleafView or ThymeleafReactiveView (template: "form" - line 3, col 21)
 

This error occurs only when using process method and will work as expected when returning "form" without @ResponseBody

 

It also works when using th:value instead of th:field.

 

And so to summarize this bug is causing errors when processing a thymeleaf template containing th:field using TemplateEngine .process method.


Reference URL: https://stackoverflow.com/questions/53761744/spring-templateengine-process-error-on-thfield

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Dec 17, 2018

Brian Clozel commented

There's no evidence that this is a bug in Spring Framework, nor Thymeleaf.

You were right to choose StackOverflow for this, let's continue there.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Dec 17, 2018

Tom commented

So i made a dive into the spring code.

 

The error is sent out from AbstractSpringFieldTagProcessor.getRequestContext, It throws TemplateProcessingException because 

IThymeleafBindStatus bindStatus = FieldUtils.getBindStatus(context, attributeValue); ends up being null.

 

bindStatus becomes null because FieldUtils.getBindStatusFromParsedExpression calls on SpringContextUtils.getRequestContext(context)

 

public static IThymeleafRequestContext getRequestContext(IExpressionContext context) {
return context == null ? null : (IThymeleafRequestContext)context.getVariable("thymeleafRequestContext");
}

and that method will return null because key "thymeleafRequestContext" does not exist.

 

It does not exist because TemplateEngine .process method will run this with a EngineContext object, this never contains a thymeleafRequestContext.

 

Normally when returning view name directly this will instead be a WebEngineContext, and that will contain a value for key "thymeleafRequestContext"

Here in this WebEngineContext the "thymeleafRequestContext" will be a SpringWebMvcThymeleafRequestContext object.

 

Spring will run with these different engines because of a check in StandardEngineContextFactory.createEngineContext

if (context instanceof IWebContext) {
IWebContext webContext = (IWebContext)context;
return new WebEngineContext(configuration, templateData, templateResolutionAttributes, webContext.getRequest(), webContext.getResponse(), webContext.getServletContext(), webContext.getLocale(), variables);
} else {
return new EngineContext(configuration, templateData, templateResolutionAttributes, context.getLocale(), variables);
}

 

WebEngineContext gets the SpringWebMvcThymeleafRequestContext object from ThymeleafView.renderFragment 

 

First part of solution here would be to make SpringWebMvcThymeleafRequestContext available for EngineContext.

So i added these lines to my method in the controller:

RequestContext requestContext = new RequestContext(request, response, servletContext, bindingMap);

SpringWebMvcThymeleafRequestContext thymeleafRequestContext = new SpringWebMvcThymeleafRequestContext(requestContext, request);

templateContext.setVariable("thymeleafRequestContext", thymeleafRequestContext);

Where bindingMap is just a map with copy of the values in templateContext.

 

Now IT WORKS!

 

Making SpringWebMvcThymeleafRequestContext available from EngineContext seems to be the solution.

 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
1 participant
You can’t perform that action at this time.