简体   繁体   中英

Spring MVC - The @SessionAttributes and status.setComplete()

I'm facing a problem I don't really know how to solve. I am developing a Bug Tracker (learning purposes only). I have a page to create a new issue and one page to edit an issue. Both, for now, have their own controllers.

EditIssueController.java

@Controller
@RequestMapping(value = "/issues/{issueId}")
@SessionAttributes("issuePackage")
public class EditIssueController {

    @Autowired
    private IssueService issueService;

    [...]

    @ModelAttribute("issuePackage")
    public IssueTagEnvironment populateIssue (@PathVariable("issueId") Integer issueId) {

        IssueTagEnvironment issueTagEnv = new IssueTagEnvironment();
        issueTagEnv.setIssue(issueService.getIssueById(issueId));

        return issueTagEnv;
    }

    @InitBinder
    public void initBinder (WebDataBinder binder) {

        [...]
    }

    @RequestMapping(value = "/edit", method = RequestMethod.GET)
    public ModelAndView editIssue (@PathVariable("issueId") Integer issueId,
                                   @ModelAttribute("issuePackage") IssueTagEnvironment issuePackage) {

        ModelAndView mav = new ModelAndView("/issues/EditIssue");

        [...]

        IssueTagEnvironment issueTagEnv = new IssueTagEnvironment();
        issueTagEnv.setIssue(issueService.getIssueById(issueId));

        [...]
        mav.addObject("issuePackage", issueTagEnv);

        return mav;
    }

    @RequestMapping(value = "/edit", method = RequestMethod.POST)
    public String updateIssue (@ModelAttribute("issuePackage") IssueTagEnvironment issuePackage,
                               BindingResult result) {

        if (result.hasErrors() == true) {
            return "redirect:/issues/{issueId}/edit";
        }

        issueService.updateIssue(issuePackage.getIssue());

        return "redirect:/issues/{issueId}";
    }
}

CreateIssueController.java

@Controller
@SessionAttributes("issuePackage")
public class CreateIssueController {

    @Autowired
    private IssueService issueService;

    [...]

    @ModelAttribute("issuePackage")
    public IssueTagEnvironment populateNewIssue () {

        return new IssueTagEnvironment();
    }

    @InitBinder
    public void initBinder (WebDataBinder binder) {

        [...]
    }

    @RequestMapping(value = "/issues/CreateIssue", method = RequestMethod.GET)
    public ModelAndView createIssueGet (@ModelAttribute("issuePackage") IssueTagEnvironment issuePackage) {

        ModelAndView mav = new ModelAndView("/issues/CreateIssue");

        [...]

        issuePackage.getIssue().setReporter(SecurityUtils.getCurrentUser());

        return mav;
    }

    @RequestMapping(value = "/issues/CreateIssue", method = RequestMethod.POST)
    public String createIssuePost (@ModelAttribute("issuePackage") IssueTagEnvironment issuePackage,
                                   BindingResult result,
                                   SessionStatus status) {

        if (result.hasErrors() == true) {
            return "redirect:/issues/CreateIssue";
        }

        [...]

        issueService.createIssue(issuePackage.getIssue());

        status.setComplete();
        return "redirect:/issues/" + issuePackage.getIssue().getId();
    }

}

So far everything seems correct (and in indeed works). But here are the dragons:

  • I am within an "edit" page changing data from an existing issue.
  • Instead of submitting the changes I decide to press the "Go Back" button from the navigator.
  • Right after that action (Go Back), I decide to create a new issue and... Here it is! The form to create a new issue isn't empty but filled with the information of the previous "edited-but-not-submitted" issue.

I understand what the problem is: The controller is not completing the session/status by executing status.setComplete() .

My question here is, how to solve this problem?

Thanks in advance to the community!

For your current example, it is easy to fix , just change createIssueGet method to :

public ModelAndView createIssueGet () {

    ModelAndView mav = new ModelAndView("/issues/CreateIssue");
    IssueTagEnvironment issuePackage = new IssueTagEnvironment();

    ModelAndView mav = new ModelAndView("/issues/CreateIssue");
    mav.addAttribute("issuePackage", issuePackage);

    [...]

    [...]
}

That way you are sure that you always use a fresh IssueTagEnvironment object in that controller. And Spring will put it in session as you put it in model.

But the problem still remains : if you do not properly call status.setComplete() , you leave in session an object that should not be there, and like you said dragons may be there

I stopped using @SessionAttributes for that reason, and only use a hidden field (for the id) and a Converter from the id to a full object using the service layer, hoping it should be in cache and does not hit the database. Not really nice, but not really worse than that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM