5

For simplicity's sake, these code snippets will be shortened. The purpose of this is to take a GET parameter, set it on the session, and redirect back to the GET with the url parameter removed. Basically, URI cleanup. If there's a better/simpler way to do this, I would be glad to hear it.

I have a controller defined as such:

@Controller
@RequestMapping("/path/page.xhtml")
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
@SessionAttributes({ "myParam1", "myParam2" })
public class MyController {

  @RequestMapping(method = RequestMethod.GET, params = { "urlParam2" })
  public String handleUriParam(@RequestParam(value = "urlParam2", required = false)
                               final Long urlParam2,
                               final RedirectAttributes redirs) {
    // at this point, myParam1 is set on the session.
    // now set the param as a flash attrib with the name of the session variable
    redirs.addFlashAttribute("myParam2", urlParam2);
    return "redirect:/path/page.xhtml";
  }

  @RequestMapping(method = RequestMethod.GET, params = {})
  public String doGetStuff(ModelMap model) {
    // do stuff using myParam1 and myParam2.
    // problem is, myParam2 is on the session, but myParam1 is not!
  }

}

Like the code says, somehow myParam1 is being un-set when the redirect happens. I can fix this by passing a ModelMap to the handleUrlParam method and manually adding myParam1 to the flash attributes, but that seems to defeat the purpose in my mind.

Why is the SessionAttribute myParam1 being removed after the redirect?

Is there a better way to pull parameters off the URI and put them on the session?

UPDATE

So it seems that whenever you use RedirectAttributes at all, you must make sure you put any SessionAttributes you want to carry into the redirect on the FlashAttributes or else they will be lost. I imagine this happens because SessionAttributes are pulled off the ModelMap (which is replaced by FlashAttributes when used). Is this a bug in Spring or intentional behavior? If it's intentional, can someone explain why? I thought SessionAttributes were meant to stay on until removed by completion of the conversational session.

Similar StackOverflow post here.

Addendum

In light of the accepted answer provided, I am still stumped as to how I can clear the URI parameters while putting them on the user's session. One option I have considered is to create a wrapper for the semi-primitive objects (java.lang.Integer, java.lang.String) I am trying to store because they will not be placed on the URI string, but this seems hacky to me. If anyone has a better way to accept GET parameters, store them on the user's session, and clear those from the user's address bar (which will require a redirect), I will gladly use it.

Community
  • 1
  • 1
Andy
  • 8,749
  • 5
  • 34
  • 59
  • I don't know what you are doing wrong, but I've just replicated your configuration and it works as expected. Both attributes end up on session (which might be strange for the second one as it was supposed to be flash attribute). – Pavel Horal Jun 14 '13 at 22:16
  • Odd. I basically gave in and set both as flash attributes and it works fine. It kind of defeats the purpose of using `@SessionAttribute`, but for regular `GET` requests, it works as expected. Thanks for looking into it. – Andy Jun 15 '13 at 16:02
  • Something I just thought of that is different from most people's configurations is the use of `BeanDefinition.SCOPE_PROTOTYPE`. Did you use that and still get correct behavior? I am curious if the creation/use of a new Controller object could be part of the problem here. Due to our interceptors and controller structure we have to use fresh objects, but it was just a thought as to whether it may be to blame for my odd behavior. – Andy Jun 15 '13 at 16:05
  • Yes, it worked with prototype scope as well. What you mean by *"for regular GET requests, it works as expected"*? And how were you setting `myParam1` on the session in the first place? Were you using `ModelMap` or directly `HttpSession`? – Pavel Horal Jun 15 '13 at 16:14
  • Now I was able to reproduce the behavior... will look into that a little deeper. – Pavel Horal Jun 15 '13 at 16:20
  • I use the POST->redirect->GET model, and myParam1 is set on the form submission for the POST prior to the GET with myParam2 attached as a GET parameter. It's not shown for brevity, but it is in the same controller. – Andy Jun 16 '13 at 00:15

1 Answers1

8

So I was looking around the code and Internet to find out why it does not work.

Spring has two completely separate model maps - one for standard view rendering and the other one when a redirect is issued. This can be observed in ModelAndViewContainer.

Now session attribute persistence is done based on the result from mavContainer#getModel(). For redirect scenarios, this returns the redirect model. Hence whatever you set on the standard Model / ModelMap is lost.

This makes sense when talking about standard model attributes. Model is there mainly to pass objects to views. When using redirects, you are dealing with a completely different situation. You want to transfer objects via HTTP redirect - hence the separated string and flash based model.

However my feeling is that they forgot about session attributes when designing this feature. There are some nice discussion in Spring's Jira, however none of them addresses this specific issue.

So yes... this might be a topic for Spring's Jira. And it might be classified as a bug as this prevents anyone from setting session model attributes when using redirect. Forcing Spring to store your session attribute via RedirectAttributes#addFlashAttribute is IMO a hack and kind of a bug on its own.

Pavel Horal
  • 17,782
  • 3
  • 65
  • 89
  • While I agree with your comment about it being a hack, I still have to clear out the URI parameters from the user's address bar. Is there a less "hacky" way to do this than how I am currently attempting it? – Andy Jun 16 '13 at 00:12
  • Also, thank you for looking into this. Your explanation is very thorough and makes perfect sense. :) – Andy Jun 16 '13 at 00:18