Spring / Thymeleaf выдает сообщение «Невозможно создать сеанс после того, как ответ был зафиксирован» при обработке @PostMapping

Я создал приложение 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 в вашем шаблоне Thymeleaf?

Branislav Lazic 25.10.2018 18:38

Строка 41 говорит: <form action = "#" class = "form" id = "newOrderForm" th: action = "@ {/ dispo / addOrder}" th: object = "$ {loadOrder}" method = "post">

Raistlin 26.10.2018 09:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
2 743
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Вам необходимо создать новый экземпляр для объекта формы

@GetMapping("/dispo/orderCreate")
private String showCreateOrder(Model model) {
    // ...
    model.addAttribute("loadOrder", new LoadOrderModel();
    return "/dispo/orderCreate";
}

Большое тебе спасибо. Но, к сожалению, это не сработало. Думаю, проблема где-то в управлении httpSession.

Raistlin 26.10.2018 09:14

Чтобы использовать th:object в форме, мы должны иметь возможность сопоставить новую сущность с этой формой. Вы можете отправить его через атрибут или вы можете установить метод, подобный приведенному ниже, в вашем контроллере, и он сделает это автоматически за вас.

@ModelAttribute(value = "loadOrder")
public LoadOrderModel newLoadOrder() {return new LoadOrderModel();}

И тебе спасибо. Я тоже попробовал ваше предложение. Но с таким же эффектом, как и с подсказкой Hithams.

Raistlin 26.10.2018 09:15
Ответ принят как подходящий

Наконец-то я это сделал. Проблема действительно заключалась в HTTP-сеансе, а точнее в HttpSecurity. Поэтому я добавил в свой метод настройки в моем классе SecurityConfig следующее:

http.sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.ALWAYS)

Мне все еще нужно выяснить, что SessionCreationPolicy.ALWAYS означает для остальной части моего приложения, но пока это работает: D.

Вы работаете как контейнер Tomcat или на другом сервере?

Sully 26.10.2018 11:44

Я запускаю приложение в контейнере Tomcat.

Raistlin 26.10.2018 11:52

@Raistlin Вы можете вкратце объяснить это?

Tony Brand 21.08.2021 11:04

Я столкнулся с той же проблемой при обновлении 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 и вернусь на эту страницу; определенно что-то связано с сеансом.

  • всегда - сеанс всегда будет создан, если он еще не существует
  • если необходимо - сессия будет создана только при необходимости (по умолчанию)
  • никогда - фреймворк никогда не создаст сам сеанс, но будет использовать его, если он уже существует
  • без гражданства - Spring Security не будет создавать или использовать сеанс

https://www.baeldung.com/spring-security-session

Другие вопросы по теме