Announcement Announcement Module
Collapse
No announcement yet.
Validate error missing in portlet render phase Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • 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, 04:55 AM.

  • #2
    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";
    }

    Comment


    • #3
      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

      Comment


      • #4
        Still not working in 3.1.1.RELEASE

        Originally posted by accumulator View Post

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

        Comment


        • #5
          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.

          Comment


          • #6
            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?

            Comment


            • #7
              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, 12:46 PM.

              Comment


              • #8
                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, 01:50 PM. Reason: added spring version

                Comment


                • #9
                  Thanks kwilkins, that worked a treat.

                  The bug is still there in 3.2.2.

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

                  Pete.

                  Comment

                  Working...
                  X