Я создал приложение Spring MVC с представлениями тимелеафа и столкнулся со следующей проблемой. У меня есть страница, которая должна обрабатывать форму и создавать новую сущность для сохранения в базе данных. В моем классе контроллера для этого есть два метода. Во-первых, @GetMapping для рендеринга страницы:
@GetMapping("/dispo/orderCreate")
private String showCreateOrder(Model model) {
List<MdUser> userList = service.getUsers();
model.addAttribute("userList", userList);
return "/dispo/orderCreate";
}
Поскольку я просто хотел показать страницу без добавления каких-либо действий в форму, все работает нормально. Атрибут модели "userList" правильно заполнен пользователями из базы данных.
Теперь я изменил представление, чтобы добавить в форму действие и объект. Код представления теперь выглядит так:
<form action = "#" class = "form" id = "newOrderForm" th:action = "@{/dispo/addOrder}" th:object = "${loadOrder}" method = "post">
<table class = "cont-table" cellpadding = "2" cellspacing = "2" width = "100%">
<tbody>
<tr align = "left">
<th align = "left" valign = "top" width = "110">Protokollführer:</th>
<td>
<table border = "0" cellpadding = "0" cellspacing = "1" width = "100%">
<tbody>
<tr>
<td height = "30">
<select class = "selectOneMenue" id = "newOrderPersoDropDown" th:field = "*{supervisor}">
<option>Bitte auswählen</option>
<option th:each = "user : ${userList}"
th:value = "user.userId"
th:text = "${user.firstName}+' '+${user.lastName}"></option>
</select>
</td>
. . .
</tr>
</tbody>
</table>
<br />
<input style = "width:200px" type = "submit" value = "Speichern" class = "commandExButton" id = "newOrderSubmit" />
<input style = "width:120px" type = "reset" value = "Zurücksetzen" class = "commandExButton" id = "newOrderReset" />
</form>
Соответствующий @PostMapping выглядит так:
@PostMapping("/dispo/addOrder")
public String submit(@ModelAttribute("loadOrder") LoadOrderModel loadOrder, BindingResult result, Model model) {
if (result.hasErrors()) {
return "error";
}
service.createAndSaveLoadOrder(loadOrder);
return "/dispo/success";
}
Теперь рендеринг представления аварийно завершается, когда форма достигается с помощью следующей трассировки стека:
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring5.processor.SpringActionTagProcessor' (template: "/dispo/orderCreate" - line 41, col 58)
at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:117) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.engine.TemplateHandlerAdapterMarkupHandler.handleOpenElementEnd(TemplateHandlerAdapterMarkupHandler.java:304) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler$InlineMarkupAdapterPreProcessorHandler.handleOpenElementEnd(InlinedOutputExpressionMarkupHandler.java:278) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.standard.inline.OutputExpressionInlinePreProcessorHandler.handleOpenElementEnd(OutputExpressionInlinePreProcessorHandler.java:186) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler.handleOpenElementEnd(InlinedOutputExpressionMarkupHandler.java:124) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.attoparser.HtmlElement.handleOpenElementEnd(HtmlElement.java:109) ~[attoparser-2.0.4.RELEASE.jar:2.0.4.RELEASE]
at org.attoparser.HtmlMarkupHandler.handleOpenElementEnd(HtmlMarkupHandler.java:297) ~[attoparser-2.0.4.RELEASE.jar:2.0.4.RELEASE]
at org.attoparser.MarkupEventProcessorHandler.handleOpenElementEnd(MarkupEventProcessorHandler.java:402) ~[attoparser-2.0.4.RELEASE.jar:2.0.4.RELEASE]
at org.attoparser.ParsingElementMarkupUtil.parseOpenElement(ParsingElementMarkupUtil.java:159) ~[attoparser-2.0.4.RELEASE.jar:2.0.4.RELEASE]
at org.attoparser.MarkupParser.parseBuffer(MarkupParser.java:710) ~[attoparser-2.0.4.RELEASE.jar:2.0.4.RELEASE]
at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:301) ~[attoparser-2.0.4.RELEASE.jar:2.0.4.RELEASE]
... 87 common frames omitted
Caused by: java.lang.IllegalStateException: Cannot create a session after the response has been committed
at org.apache.catalina.connector.Request.doGetSession(Request.java:3030) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.connector.Request.getSession(Request.java:2468) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:896) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:908) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:240) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:240) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
at org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.saveToken(HttpSessionCsrfTokenRepository.java:63) ~[spring-security-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.security.web.csrf.LazyCsrfTokenRepository$SaveOnAccessCsrfToken.saveTokenIfNecessary(LazyCsrfTokenRepository.java:175) ~[spring-security-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.security.web.csrf.LazyCsrfTokenRepository$SaveOnAccessCsrfToken.getToken(LazyCsrfTokenRepository.java:127) ~[spring-security-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor.getExtraHiddenFields(CsrfRequestDataValueProcessor.java:71) ~[spring-security-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.thymeleaf.spring5.context.webmvc.SpringWebMvcThymeleafRequestDataValueProcessor.getExtraHiddenFields(SpringWebMvcThymeleafRequestDataValueProcessor.java:80) ~[thymeleaf-spring5-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.spring5.requestdata.RequestDataValueProcessorUtils.getExtraHiddenFields(RequestDataValueProcessorUtils.java:79) ~[thymeleaf-spring5-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.spring5.processor.SpringActionTagProcessor.doProcess(SpringActionTagProcessor.java:118) ~[thymeleaf-spring5-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.standard.processor.AbstractStandardExpressionAttributeTagProcessor.doProcess(AbstractStandardExpressionAttributeTagProcessor.java:142) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
... 100 common frames omitted
Строка 41, обозначенная TemplateProcessingException, является строкой с тегом формы. У меня почти нет опыта разработки внешнего интерфейса, поэтому, пожалуйста, наберитесь терпения. Я думаю, что здесь мне нужно управлять сеансом http, но я не знаю, что делать и как это делать. Может кто-нибудь мне помочь?
Строка 41 говорит: <form action = "#" class = "form" id = "newOrderForm" th: action = "@ {/ dispo / addOrder}" th: object = "$ {loadOrder}" method = "post">
Вам необходимо создать новый экземпляр для объекта формы
@GetMapping("/dispo/orderCreate")
private String showCreateOrder(Model model) {
// ...
model.addAttribute("loadOrder", new LoadOrderModel();
return "/dispo/orderCreate";
}
Большое тебе спасибо. Но, к сожалению, это не сработало. Думаю, проблема где-то в управлении httpSession.
Чтобы использовать th:object
в форме, мы должны иметь возможность сопоставить новую сущность с этой формой. Вы можете отправить его через атрибут или вы можете установить метод, подобный приведенному ниже, в вашем контроллере, и он сделает это автоматически за вас.
@ModelAttribute(value = "loadOrder")
public LoadOrderModel newLoadOrder() {return new LoadOrderModel();}
И тебе спасибо. Я тоже попробовал ваше предложение. Но с таким же эффектом, как и с подсказкой Hithams.
Наконец-то я это сделал. Проблема действительно заключалась в HTTP-сеансе, а точнее в HttpSecurity. Поэтому я добавил в свой метод настройки в моем классе SecurityConfig следующее:
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
Мне все еще нужно выяснить, что SessionCreationPolicy.ALWAYS означает для остальной части моего приложения, но пока это работает: D.
Вы работаете как контейнер Tomcat или на другом сервере?
Я запускаю приложение в контейнере Tomcat.
@Raistlin Вы можете вкратце объяснить это?
Я столкнулся с той же проблемой при обновлении Spring Boot 1.x до 2.x (который также включает более новую версию Thymeleaf). Моя проблема заключалась в том, что Zhymeleaf пытался автоматически создать скрытый ввод csrf для каждой формы html. Сгенерированные токены csrf необходимо было сохранить в сеансе http.
Включение сеансов (как показано @Raistlin) или отключение защиты csrf (http.csrf().disable()
) решило проблему для меня.
Вы также можете ограничить токен csrf определенными страницами, используя http.csrf().requireCsrfProtectionMatcher(..)
или http.csrf().ignoringRequestMatchers(..)
.
У меня тоже была такая же проблема; применил предложение @Raistlin, и теперь он работает нормально !!
У меня был модальный диалог с "form-post" на странице отправки. Это не удастся, если я сначала попробую модальный. И отлично работает, если я сначала попробую открыть страницу GET и вернусь на эту страницу; определенно что-то связано с сеансом.
Что находится в строке 41 в вашем шаблоне Thymeleaf?