Spring TemplateEngine在th:字段上处理错误



我正在使用spring用Java开发一个web应用程序。

该应用程序包括javascript中的Ajax调用,该调用请求html代码,然后将html代码插入到html文档中。

为了将thymelaf模板处理成String,我使用TemplateEngine process(..(方法。

当thymelaf模板包含表单时,我遇到了一个错误。

我的样本代码:

form.html:

<form th:object="${customer}" xmlns:th="http://www.w3.org/1999/xhtml">
<label>Name</label>
<input type="text" th:field="*{name}" />
</form>

AjaxController.java:

package project;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
@Controller
public class AjaxController {
@Autowired
private TemplateEngine templateEngine;
private ObjectMapper objectMapper = new ObjectMapper();
@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);
response.additionalData = "ab123";
return objectMapper.writeValueAsString(response);
}
@GetMapping(value="/form2")
public String form2(Model model) throws JsonProcessingException {
Customer customer = new Customer("Burger King");
model.addAttribute("customer", customer);
return "form";
}
class Customer {
private String name;
public Customer(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class AjaxResponse {
public String html;
public String additionalData;
}
}

form1崩溃了,我试图返回thymelaf模板解析的html代码,并在这个json响应中包含额外的数据。它在templateEngine.process("form",templateContext(行崩溃;

将form.html替换为:时,form1有效

客户名称为:[[${Customer.name}]]

这让我得出结论,是表单标记和th:object导致了崩溃。

form2的工作与预期的一样,但没有任何方法来操作thymelaf返回值。它证明了thymelaf模板本身是有效的。

整个错误输出有点太大,无法粘贴在这里,但是:

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)

我的问题是:这是spring框架中的一个bug吗?否则我做错了什么?


更新1:将th:field替换为th:value可以使其工作,当使用TemplateEngine.produce时,表单中的th:field似乎会产生错误。

更新2:好吧,经过大量的侦探工作,我想出了一种临时的破解方法。问题是thymelaf需要IThymeleafRequestContext来处理带有表单的模板,当TemplateEngine.process运行时,将不会创建该模板。可以将其注入到您的模型中,如下所示:

@Autowired
ServletContext servletContext;
private String renderToString(HttpServletRequest request, HttpServletResponse response, String viewName, Map<String, Object> parameters) {
Context templateContext = new Context();
templateContext.setVariables(parameters);
RequestContext requestContext = new RequestContext(request, response, servletContext, parameters);
SpringWebMvcThymeleafRequestContext thymeleafRequestContext = new SpringWebMvcThymeleafRequestContext(requestContext, request);
templateContext.setVariable("thymeleafRequestContext", thymeleafRequestContext);
return templateEngine.process(viewName, templateContext);
}

现在你使用这样的方法:

@ResponseBody
@GetMapping(value="/form1")
public String form1(HttpServletRequest request, HttpServletResponse response) throws JsonProcessingException {
Customer customer = new Customer("Burger King");
BindingAwareModelMap bindingMap = new BindingAwareModelMap();
bindingMap.addAttribute("customer", customer);
String html = renderToString(request, response, "form", bindingMap);
AjaxResponse resp = new AjaxResponse();
resp.html = html;
resp.additionalData = "ab123";
String json = objectMapper.writeValueAsString(resp);
return json;
}

我不会把它作为一个答案,因为我看不出有任何理由这样使用它。我正在与春天的人们沟通,以获得真正的解决方案。

欢迎使用SO.

form标记中删除xmlns:th="http://www.w3.org/1999/xhtml"。这不是正确的语法。这将属于html标记。

你可以在文档中找到很多清晰的例子。

您似乎试图在web请求上下文之外手动呈现HTML模板,并将其序列化为AJAX响应,但仍然希望表单绑定能够工作。这是这里的关键问题。

在模板中使用th:field意味着您期望从HTTP请求进行表单绑定。在您的代码片段中,您提供了一个空的非web上下文,并且仍然期望发生表单绑定。

由于Thymelaf可以在各种情况下使用(例如在发送新闻稿之前呈现电子邮件模板,在批处理应用程序中呈现文档(,因此我们不能在所有情况下都强制执行web上下文。

当以Spring Framework期望的方式呈现视图时(通过返回视图名称作为控制器处理程序的返回值(,Spring将相应地使用和配置Thymelaf。

您的答案在技术上是有效的,因为它解决了您的问题,但它来自于呈现模板并将其包装成json字符串的复杂约束,并且仍然期望HTTP绑定。

最新更新