Rob Shepherd
Rob Shepherd

Reputation: 937

spring @SessionAttributes and "Cannot create a session after the response has been committed"

I am trying to set and get a session attribute, but either I have misunderstood how it works or i've done something wrong.

Please could somebody explain why this doesn't work.

I have the following simple Spring Web MVC Controller

@Controller
@SessionAttributes("sessid")
@RequestMapping("sesstest")
public class SessTest 
{
    @RequestMapping("in")
    @ResponseBody
    public String in(ModelMap model){
        String uuid = UUID.randomUUID().toString();
        model.addAttribute("sessid", uuid);
        return uuid;
    }

    @RequestMapping("out")
    @ResponseBody
    public String out(@ModelAttribute("sessid") String sesid){
        return sesid;
    }
}

This class should just set a session variable when calling /sesstest/in and then recall it when calling /sesstest/out

But It get the following exception when calling /in

(The stacktrace is shown on the console, the web output is OK)

java.lang.IllegalStateException: Cannot create a session after the response has been committed
    at org.apache.catalina.connector.Request.doGetSession(Request.java:2872)
    at org.apache.catalina.connector.Request.getSession(Request.java:2249)
    at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:895)
    at org.springframework.web.context.request.ServletRequestAttributes.getSession(ServletRequestAttributes.java:111)
    at org.springframework.web.context.request.ServletRequestAttributes.setAttribute(ServletRequestAttributes.java:161)
    at org.springframework.web.bind.support.DefaultSessionAttributeStore.storeAttribute(DefaultSessionAttributeStore.java:55)
    at org.springframework.web.method.annotation.SessionAttributesHandler.storeAttributes(SessionAttributesHandler.java:124)
    at org.springframework.web.method.annotation.ModelFactory.updateModel(ModelFactory.java:232)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.getModelAndView(RequestMappingHandlerAdapter.java:879)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:782)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:705)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1086)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:659)
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

Thanks!

Upvotes: 4

Views: 5830

Answers (1)

Rob Shepherd
Rob Shepherd

Reputation: 937

Officially this "works as designed".

In this scenario, the various workarounds are:

  1. Manually start a session
  2. Use a filter to ensure a session is present
  3. Use a view render from the model.

See Rossen's clear response here: https://jira.spring.io/browse/SPR-12877

The scenario is somewhat unusual since typically model attributes are used for rendering with views (e.g. HTML template). For @ResponseBody only the return value is used to render the response. That said the use of @ResponseBody doesn't preclude the use of @SessionAttributes. The error has to do with trying to create the session for the first time too late. Normally the use of something like Spring Security with a Filter to store authentication in the session ensures there is always an HTTP session. In the absence of that you could add your own Filter or HandlerInterceptor that simply calls request.getSession(true). That's enough to ensure a session is present.

Can't argue with that.

Upvotes: 6

Related Questions