As every one probably knows Flash Scope would be very useful feature in every modern web framework. It allows us to use POST/Redirect/GET design pattern which solves a lot of issues associated with handling of page Multiple submits or resubmission data to browsers.

Unfortunately there was no flash scope implementation in Spring prior to 3.1, so the only option was to set interceptor which will do all the works for us. You can read more about it in my StackOverflow post.

Luckily for us in Spring MVC 3.1 the picture was changed and RedirectAttributes class was introduced. Let’s see some code example.

 

Let’s consider that we have very simple registration form. when user submits his details it will be saved on some database and registration confirmation message will appear on the page.

This article goal is indeed to demonstrate FlashScope feature, so we will skip all Bean and duplicate user name/mail validation.

 

Our form looks like this and is very simple:

 

<form:form id="myform" action="saveUserDetails.action" method="POST" commandName="user">
   <form:input type="text" name="firstName" path="firstName"/>
   <form:input type="text" name="lastName" path="lastName"/>
   <form:input type="text" name="email" path="email"/>
   <input type="submit" value="submit">
</form:form>

Code which maps saveUserDetails.action:

        @RequestMapping(value="/saveUserDetails.action", method=RequestMethod.POST)
	public String greetingsAction(@Validated User user,RedirectAttributes redirectAttributes){
                //Save user object.
                someUserdetailsService.save(user);
		//setting attributes 
                redirectAttributes.addFlashAttribute("firstName", user.getFirstName());
		redirectAttributes.addFlashAttribute("lastName", user.getLastName())
		return "redirect:success.html";
	}

As you can see we are passing RedirectAttributes object as a parameter to our greetingAction method and are just using it to add all our flash scope parameters which will be required right after redirect to success.html.

Let’s see our success.hml controller method: 

	@RequestMapping(value="/success.html", method=RequestMethod.GET)
	public String successView(){
		return "success";
	}

We don’t have any mention of RedirectAttributes  object here. So how do we  fetch it you would ask? This answer hidden in success.html view jsp page. We simply print it in jstl manner:

<div>
<h1>Hello ${firstName} ${lastName}. Your details stored in our database.</h1> 
</div>

Looks fantastic, However you need to know something before you start to use it.


1) You should have RequestMappingHandlerMapping, RequestMappingHandlerAdapter, and ExceptionHandlerExceptionResolver enabled. you might enable it using:

<mvc:annotation:driven />
in Dispatcher Servlet XML file.  

 

2) Pre Spring MVC 2 users were using ModelAndView object which would be returned to Controller from request mapped function. After Spring 2, this approach is still supported, but the preferred way would be to return String with the View Name (as we did in our examples above).I have some bad news for ModelAndView funs - RedirectAttributes will not work if ModelAndView are used in controller.  

 

Our example works well, but it has some flow. What would happen if F5 or refresh button would be used in our browser, after flash attributes were printed? 

Yep, you are right. It will print userName and password as null values, and the reason for that is simple - Flash Scope supports only one redirect and its attribute are automatically cleared after redirect. 

Luckily, we can solve it in very simple way. We would check whether  inputFlashMap exists in request or not. if it does, then the current GET request is part of Post/Redirect/Get design pattern and if it’s not then user got this page by mistake and we will redirect him from there.

 

@RequestMapping(value="/success.html", method=RequestMethod.GET)
	public String successView(HttpServletRequest request){
		Map<String,?> map = RequestContextUtils.getInputFlashMap(request); 
		if (map!=null)
		   return "success";
		else return "redirect:someOtherView"; 

 

1

Comments