【问题标题】:Spring/Thymeleaf throws "Cannot create a session after the response has been committed" when processing @PostMappingSpring/Thymeleaf 在处理@PostMapping 时抛出“在响应提交后无法创建会话”
【发布时间】:2018-10-25 05:57:40
【问题描述】:

我使用 thymeleaf 视图构建了一个 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

TemplateProcessingException 指示的第 41 行是带有表单标记的行。 我几乎没有前端开发经验,所以请耐心等待。我想我必须在这里进行一些 http 会话管理,但不知道该做什么以及如何去做。谁能帮帮我?

【问题讨论】:

  • Thymeleaf 模板的第 41 行是什么?
  • 第 41 行说:

标签: spring-mvc spring-boot thymeleaf


【解决方案1】:

我终于成功了。问题确实出在 http 会话中,或者更准确地说,在 HttpSecurity 中。所以我在我的 SecurityConfig 类的 configure 方法中添加了以下内容:

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

我仍然需要找出 SessionCreationPolicy.ALWAYS 对我的应用程序的其余部分意味着什么,但现在它可以工作了:D。

【讨论】:

  • 您是作为 Tomcat 容器运行还是在其他服务器上运行?
  • 我在 Tomcat 容器上运行应用程序。
  • @Raistlin 你能简单解释一下吗?
【解决方案2】:

我也遇到了同样的问题;应用了@Raistlin 的建议,现在可以正常工作了!!

我在提货页面中有一个带有“form-post”的模态对话框。如果我先尝试模态,它将失败。如果我先尝试 GET 页面,然后返回此页面,则效果很好;肯定与会话有关。

  • 总是 – 如果会话不存在,则始终创建会话
  • ifRequired – 只有在需要时才会创建会话(默认)
  • 从不 – 框架永远不会自己创建会话,但如果它已经存在,它会使用它
  • 无状态 - Spring Security 不会创建或使用任何会话

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

【讨论】:

    【解决方案3】:

    您需要为表单对象创建新实例

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

    【讨论】:

    • 非常感谢。但不幸的是,它没有奏效。我认为问题出在 httpSession 管理的某个地方。
    【解决方案4】:

    为了在表单中使用th:object,我们必须能够将新实体映射到该表单。您可以通过属性发送它,也可以在控制器中设置如下方法,它会自动为您完成。

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

    【讨论】:

    • 也谢谢你。我也试过你的建议。但效果与 Hithams 提示相同。
    【解决方案5】:

    我在从 Spring Boot 1.x 升级到 2.x(还包括较新的 Thymeleaf 版本)时遇到了同样的问题。 我的问题是 Thymeleaf 试图为每个 html 表单自动创建一个隐藏的 csrf 输入。生成的 csrf 令牌需要在 http 会话中持久化。

    启用会话(如 @Raistlin 所示)或禁用 csrf 保护 (http.csrf().disable()) 为我解决了这个问题。

    您还可以使用 http.csrf().requireCsrfProtectionMatcher(..)http.csrf().ignoringRequestMatchers(..) 将 csrf 令牌限制到某些页面

    【讨论】:

      猜你喜欢
      • 2014-12-08
      • 1970-01-01
      • 2012-06-12
      • 2012-06-06
      • 2013-05-29
      • 2021-09-24
      • 1970-01-01
      • 2013-12-02
      相关资源
      最近更新 更多