Results 1 to 9 of 9

Thread: Validate error missing in portlet render phase

  1. #1
    Join Date
    Nov 2010
    Posts
    5

    Default Validate error missing in portlet render phase

    I'm having a problem regarding the validation error lost in render phase, but not sure whether this is bug in spring portlet mvc.

    When The validation is done in action phase, the error will be store in the model attribute. However, if the model attribute was replaced in render phase, the errors that result from action phase was reset. Hence the error message cannot be display on jsp. In the example following, I have a jsp with one form input which is 'secondaryEmail' and the modelAttribute is "emailForm".

    Code:
    public class PersonalDetailsController {
    	
    	private static final Log LOGGER = LogFactory.getLog(PersonalDetailsController.class);
    
    	@ModelAttribute("emailForm")
    	public EmailForm getEmailForm() {
    		return new EmailForm();
    	}
    	
    	// --maps the incoming portlet request to this method
    	@RenderMapping
    	public String showDefaultPage(RenderRequest request, Model model) {
    		
    		Map<String, Object> modelMap =  model.asMap();
    		
    		Set<String> keys = modelMap.keySet();
    		
    		for(String key: keys){
    			LOGGER.info("Render phase 1 ===> Model attribute:  Map key = ["+key+"]; value = ["+modelMap.get(key)+"]");				
    		}
    
    		model.addAttribute("emailForm", new EmailForm());
    		
    		Map<String, Object> newModelMap =  model.asMap();
    		
    		keys = newModelMap.keySet();
    		
    		for(String key: keys){
    			LOGGER.info("Render model ===========> Map key: "+key+"; value: "+newModelMap.get(key));				
    		}
    		
    		return "personalDetails";		
    	}
    
    	@ActionMapping(params = "myaction=editEmail")
    	public void editEmail(ActionRequest request, ActionResponse response, Model model,
    			@ModelAttribute("emailForm") EmailForm emailForm, BindingResult result) {		
    
    		LOGGER.info("+++ Updating email now. +++ ====> email: "+ emailForm.getSecondaryEmail());
    		
    		ValidationUtils.rejectIfEmptyOrWhitespace(result, "secondaryEmail", "email.required", "Email cannot be empty!!!");
    		
    		if (result.hasErrors()) {
    
    			LOGGER.error("+++ Error Validate +++");
    			
    			Map<String, Object> modelMap =  model.asMap();
    			Set<String> keys = modelMap.keySet();
    			
    			for(String key: keys){
    				LOGGER.info("Action: ======> Map key: "+key+"; value: "+modelMap.get(key));				
    			}
    
    			return;
    		}		
    	}
    when the secondaryEmail was left blank and submit, the output log is as following. the log output:
    17:34:43,735 INFO [STDOUT] 17:34:43,735 INFO [PersonalDetailsController] +++ Updating email now. +++ ====> email: null
    17:34:43,737 INFO [STDOUT] 17:34:43,737 ERROR [PersonalDetailsController] +++ Error Validate +++
    17:34:43,737 INFO [STDOUT] 17:34:43,737 INFO [PersonalDetailsController] Action: ======> Map key: emailForm; value: secondaryEmail:[null]
    17:34:43,737 INFO [STDOUT] 17:34:43,737 INFO [PersonalDetailsController] Action: ======> Map key: org.springframework.validation.BindingResult.email Form; value: org.springframework.validation.BeanPropertyBinding Result: 1 errors
    Field error in object 'emailForm' on field 'secondaryEmail': rejected value [null]; codes [email.required.emailForm.secondaryEmail,email.requ ired.secondaryEmail,email.required.java.lang.Strin g,email.required]; arguments []; default message [Email cannot be empty!!!]
    17:34:43,765 INFO [STDOUT] 17:34:43,765 INFO [PersonalDetailsController] Render phase 1 ===> Model attribute: Map key = [emailForm]; value = [secondaryEmail:[null]]
    17:34:43,765 INFO [STDOUT] 17:34:43,765 INFO [PersonalDetailsController] Render phase 1 ===> Model attribute: Map key = [org.springframework.validation.BindingResult.email Form]; value = [org.springframework.validation.BeanPropertyBinding Result: 1 errors
    Field error in object 'emailForm' on field 'secondaryEmail': rejected value [null]; codes [email.required.emailForm.secondaryEmail,email.requ ired.secondaryEmail,email.required.java.lang.Strin g,email.required]; arguments []; default message [Email cannot be empty!!!]]

    17:34:43,765 INFO [STDOUT] 17:34:43,765 INFO [PersonalDetailsController] Render model ===========> Map key: emailForm; value: secondaryEmail:[null]

    ----- end of log -----
    So what I assume is that:
    when
    model.addAttribute("emailForm", new EmailForm());
    was run, the validation error that bind to model attribute "emailForm" was lost.

    But the main problem is that if I want to get the "emailForm" attribute in render phase by adding one more argument to the render method

    Code:
    public String showDefaultPage(RenderRequest request, Model model, @ModelAttribute("emailForm") EmailForm emailForm) {
    		
    		Map<String, Object> modelMap =  model.asMap();
    		
    		Set<String> keys = modelMap.keySet();
    		
    		for(String key: keys){
    			LOGGER.info("Render phase 1 ===> Model attribute:  Map key = ["+key+"]; value = ["+modelMap.get(key)+"]");				
    		}
    
    		model.addAttribute("emailForm", new EmailForm());
    		
    		Map<String, Object> newModelMap =  model.asMap();
    		
    		keys = newModelMap.keySet();
    		
    		for(String key: keys){
    			LOGGER.info("Render model ===========> Map key: "+key+"; value: "+newModelMap.get(key));				
    		}
    		
    		return "personalDetails";		
    	}
    the validation result will be reset also and this
    LOGGER.info("Render phase 1 ===> Model attribute: Map key = ["+key+"]; value = ["+modelMap.get(key)+"]");
    will show the bindingResult has no error.

    What is the proper solution if I would like to use the model attribute in render phase?

    For temporary solution, what I do is make the model attribute "emailForm" session attribute

    Code:
    @SessionAttributes({"emailForm"})
    public class PersonalDetailsController{
    ...
    public String showDefaultPage(RenderRequest request, Model model) {
       ...
       EmailForm form = (EmailForm)PortletUtils.getSessionAttribute(request, "emailForm", 
    				PortletSession.PORTLET_SCOPE);
       ...
    }
    ...
    and get the model attribute through PortletUtils as

    Code:
    PortletUtils.getSessionAttribute(request, "emailForm", PortletSession.PORTLET_SCOPE);
    in render phase.

    What is the proper way to do this?
    Should I log this as bug in jira?

    I'm using spring-webmvc-portlet-3.0.4.RELEASE
    Thanks
    Last edited by wssoh; Nov 24th, 2010 at 04:55 AM.

  2. #2
    Join Date
    Nov 2010
    Posts
    5

    Default

    Just to add-on

    if the model attribute was not call in render phase as following, the validation error message will be able to display in jsp

    public class PersonalDetailsController{
    ...
    public String showDefaultPage(RenderRequest request, Model model) {
    ...
    return "personalDetails";
    }

  3. #3
    Join Date
    Jan 2011
    Posts
    1

    Default is this a bug?

    interesting.. I see the same thing here.

    Errors get set by validation in a ActionMapping method with signature (@ModelAttribute Form form, BindingResult result)

    in the RenderMapping method I get different behaviour depending on the method signature
    - () : errors set (as expected)
    - (@ModelAttribute Form form) : no errors set (unexpected)
    - (@ModelAttribute Form form, BindingResult result) : no errors set (might be expected because of re-association with BindingResult)

    using spring MVC 3.0.5-RELEASE

  4. #4
    Join Date
    Sep 2009
    Posts
    5

    Default Still not working in 3.1.1.RELEASE

    Quote Originally Posted by accumulator View Post

    using spring MVC 3.0.5-RELEASE
    This is still "broken" in 3.1.1-RELEASE.

  5. #5

    Thumbs up Extra Render does the trick

    I faced same issue , then i have created separate render method without any model attribute for error handling and binding result able to reach to jsp via render method.

  6. #6

    Default

    Quote Originally Posted by mahipalsinh.rana View Post
    I faced same issue , then i have created separate render method without any model attribute for error handling and binding result able to reach to jsp via render method.
    So when the command and binding result params exist in the method signature, spring retrieves and populates the command object correctly from the session but "resets" the BindingResult param - like it's trying to perform another binding.

    Does anyone know a JIRA for this issue or are we following the wrong pattern?

  7. #7
    Join Date
    Jul 2009
    Posts
    19

    Default

    My solution to this issue was to use the @Valid annotation on the @ModelAttribute in the @RenderMapping like its being done in the @ActionMapping. Unfortunately, this causes the validation to occur twice, but I couldn't find another way to get the binding from the action phase to carry over to the render phase.

    Additionally, you need a check to make sure the form is initialized before you validate.
    Last edited by whardwick; Jul 3rd, 2012 at 12:46 PM.

  8. #8
    Join Date
    Jul 2012
    Location
    Irvine, CA
    Posts
    5

    Default

    My solution to this issue was when there are validation/binding errors in the @ActionMapping to redirect back to the @RenderMapping and add a check to see if the formobject is already present in the model from your @ActionMapping that was just called(the Model isn't cleared out on a redirect). If it is present, then just don't overwrite it(or you can re-add at it, I believe the binding erros are associated with that formobject's eq() but not totally sure) and when the @RenderMapping renders the page the validation/binding errors do properly show within the form. This is working with Spring 3.1.1

    Here is a rough example of it in my portlet:

    Code:
    @RenderMapping(params="action=editPrint")
    public String editPrintItem(Model model, RenderRequest request, RenderResponse response) {
    
    	Print printToEdit;
    	if(model.containsAttribute("print")) {
    		printToEdit = (Print) model.asMap().get("print");
    	} else {
    		// adding a new print item
    		printToEdit = new Print();
    	}
    		
    	model.addAttribute("print", printToEdit);
    	return "author_editPrint";
    }
    
    	
    @ActionMapping("savePrint")
    public void savePrintItem(Model model, ActionRequest request, ActionResponse response,
    		@ModelAttribute("print") @Valid Print print,
    		BindingResult result) {
    		
    	if(result.hasErrors()) {
    		response.setRenderParameter("action", "editPrint");
    	} else {
    		print.setLastUpdatedBy(request.getRemoteUser());
    		service.persist(print);
    		response.setRenderParameter("action", "printManagement");
    	}
    }
    Last edited by kwilkins; Jul 5th, 2012 at 01:50 PM. Reason: added spring version

  9. #9
    Join Date
    Apr 2010
    Posts
    1

    Default

    Thanks kwilkins, that worked a treat.

    The bug is still there in 3.2.2.

    https://jira.springsource.org/browse/SPR-9560

    Pete.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •