Results 1 to 10 of 17

Thread: Do binding errors prevent validation?

Hybrid View

  1. #1
    Join Date
    Sep 2008
    Posts
    1

    Default Do binding errors prevent validation?

    I have previously implemented Spring Portlet MVC applications and have hit a snag trying to implement a validator under SWF 2.0.3

    My validator is a fairly simple affair:

    Code:
    public class ProjectFormValidator {
    
    	public void validateActivityDetails(ProjectForm form, Errors errors) {
    		rejectIfEmptyOrWhitespace(errors, "projectDetail.projectName", form.getProjectDetail().getProjectName(), RdtcConstants.REQUIRED_FIELD_KEY);
    	}
    	
    	private void rejectIfEmptyOrWhitespace(Errors errors, String field, Object fieldValue, String requiredKey){
    		//Object value = errors.getFieldValue(path);
    		if (fieldValue == null ||!StringUtils.hasText(fieldValue.toString())) {
    			errors.rejectValue(field, requiredKey, null, null);
    		}
    	}
    	
    }
    I'm using an Errors instance in the method as opposed to a MessageContext instance as I have a bunch of legacy validation routines that depend on Errors. Based on previous portlet MVC experience, I'd expect my validator to be invoked after binding is complete, with any binding errors pre-populated into the Errors object and with the ability to call errors.getFieldValue() (which should return either the currently bound field value or the user-entered value if a binding error occurred).

    After configuring my validator in the spring and webflow configuration files I'm experiencing two separate but potentially related problems:

    1. My validator is ONLY invoked if no data binding errors occurred. This causes usability issues in our application, as a user will initially be presented with a set of binding-related errors, and after correcting these and re-submitting will potentially be presented with a new, unrelated set of errors (from the validator) that we could/should have been able to tell them about the first time.

    2. Attempting to call errors.getFieldValue() throws an UnsupportedOperationException. I believe this has been previously raised as SWF-823 and looks like it will be fixed in the 2.0.4 release, but it is unclear to me whether the fix just prevents the exception being thrown or whether it will return the user-entered input in the case of a binding failure as specified in the Errors javadoc.

    So are my expectations incorrect, am I missing something else or is there a problem in the framework?

  2. #2
    Join Date
    Oct 2008
    Posts
    4

    Default Binding and validation functionality previousily available SWF been lost in SWF 2.0.3

    In using SWF 2.0.3 we have lost some functionality during binding and validation of forms that are submitted to the portal server. This functionality is available in previous version SWF and in the Spring Portlet MVC.

    We have discovered that binding and validation model in SWF 2.0.3 has changed from that used in the previous SWF 1.0 and the standard Spring Portal MVC.

    We can now no longer provide the following functionality

    1. Binding and validation messages in one list, because the checks are no longer done together.

    This means that there are seperate error lists for binding errors and for validation errors.
    With the binding error list being returned first and then the validation checks being perform once all the binding errors have been resolved.

    For the user this means that they will possibly get two successive lists of errors on the html form that must be corrected before they can successfully submit the form to the Portal. In the previous version of the SWF and also the standard Spring Porlet MVC combined the error list from binding and validation together and returned it to the user as one list to be resolved.

    2. In the case where there are binding errors. The fields that have binding errors will be highlighted but are not populated with the data that caused the binding error. Instead the value in the fields will be reverted back to what ever the pervious value they were originally bound to.

    For the end user this means that they will not see the value that they supplied that caused the binding error. And may even be potentially confused by seeing valid data highlighted as an being in error.

    The previous version of SWF and the standard Spring portlet MVC both populate the HTML form field with the data that caused the binding error.

  3. #3
    Join Date
    Aug 2004
    Location
    Melbourne, FL
    Posts
    2,794

    Default

    Indeed, the Web Flow 2 model validation functionality will not invoke the configured Validator if binding also fails. I have indeed noticed this is inconsistent with FormAction's original "bindAndValidate" method, which always performs validation after binding, even if there are validation errors.

    The con I see with performing validation after binding when there are binding errors is the potential for duplicate error messages. For example, if a type conversion occurs on a new form against a required field, you'll end up with a type conversion error message and a required error message. However, I think the pro's [preserved behavior + more intuitive usability] outweigh this con.

    Thoughts?

    Keith
    Keith Donald
    Core Spring Development Team

  4. #4
    Join Date
    Oct 2008
    Posts
    4

    Default

    Thanks for your response Keith, we thought this issue was a goner and have coded around it.

    We agree
    [preserved behavior + more intuitive usability] outweigh this con.

    Regarding the duplicate validation problem you describe:
    For example, if a type conversion occurs on a new form against a required field, you'll end up with a type conversion error message and a required error message.
    In spring MVC you would use the errors.getFieldValue() method which returns the bound value or the user entered value if binding failed. Validating against this value would prevent a required error being raised when a binding error occurs.

    For us, the issue described in number 2, above is our biggest problem. In that the value supplied by the user that is causing the binding error is not being bound back to web form when we display our error message. Instead the last valid value bound is put in its place. WE assume this stems from the same problem in that framework cannot use the errors.getFieldValue() method to get the user entered value if binding failed.

    This manifests its self as following usability issue in our application.
    e.g. The user is presented with an html form that takes a pre populated dollar value:


    Research: how much did you plan to spend ? $[ 5000]

    the user enters 7,500 (note: the comma will cause a binding error)

    e.g.
    Research: how much did you plan to spend ? $[ 7,500]



    In this case our user will see a html form with the error message as follows.
    e.g.

    Error - You cannot continue because there is a problem with the information submitted. Please read the message(s) below, make the necessary changes and then continue.

    Research: Please enter a valid dollar amount in whole dollars only (without cents)


    Research: how much did you plan to spend ? $[ 5000]


    Note the existing valid value is bound to the html form rather than the error value 7,500 with a comma that causes the binding error.

  5. #5
    Join Date
    Aug 2004
    Location
    Melbourne, FL
    Posts
    2,794

    Default

    Thanks for the useful information / context. I am not seeing the #2 issue as you describe it. Erred values that resulting in binding failures should definitely be rendered when the form re-renders so the user can revise their edits. I confirmed this behavior in our reference application. For example, disable JavaScript and try typing in "bogus" for a booking Check In date... when the form re-renders with validation errors, "bogus" is re-rendered instead of the previous valid-value. Is it possible we are thinking of a binding failure as different things? A binding failure occurs when attempting to copy the user-entered value to the target bean, and that copy process fails, leaving the target bean property unmodified. The original user entered value is then tracked in what we call a MappingResult object, and that original value is then exported to the view by our MVC adapter BindingModel [an Errors implementation]. You can see that code here:
    Code:
    public Object getFieldValue(String field) {
    	if (mappingResults != null) {
    		List results = mappingResults.getResults(new FieldErrorResult(field));
    		if (!results.isEmpty()) {
    			MappingResult fieldError = (MappingResult) results.get(0);
    			return fieldError.getResult().getOriginalValue();
    		}
    	}
    	return getFormattedValue(parseFieldExpression(field));
    }
    Would you mind putting a breakponit in this method of BindingModel and see what is going on for your amount field in the form that is having this problem? Is this problem always happening or just sometimes? Have you tried upgrading to Web Flow 2.0.4? As Richard mentioned in Web Flow 2 you can now use the Errors.getFieldValue(String) method to access the original value from your Validator. Thanks Keith
    Last edited by Keith Donald; Nov 11th, 2008 at 09:09 AM.
    Keith Donald
    Core Spring Development Team

  6. #6

    Default

    Hi Keith,

    in my opinion it would be the best way if it's configurable if validation should be executed after binding errors occurs. So the developer can decide which way to choose. In our projects we have often the case that customers wanna see binding errors and valdation error togther.

    Regards,

    Flashrider

  7. #7
    Join Date
    Oct 2008
    Posts
    4

    Default

    Hi Keith,

    Is it possible we are thinking of a binding failure as different things?
    No I think we are on the same page regarding the binding errors. And your test gives the behaviour that we expect. It is good that that the reference implementation is working okay.

    However one thing I think we failed to mention is that we are having this problem in a portal server environment.
    We have tried running with the break point set and the mappingResults == null therefore we don't drop into the code that fetches the get the original value
    Code:
    if (mappingResults != null) {
    		List results = mappingResults.getResults(new FieldErrorResult(field));
    		if (!results.isEmpty()) {
    			MappingResult fieldError = (MappingResult) results.get(0);
    			return fieldError.getResult().getOriginalValue();
    		}
    	}
    Could they MappingResults be getting lost between the action request and render request in the portal environment ?

    Also we have tried deploying the reference application into the pluto portal server and we get the following error when we try the bogus example

    Spring Webflow Booking MVC
    Code:
    Error rendering portlet.
    javax.portlet.PortletException: Request processing failed
    	at org.springframework.web.portlet.FrameworkPortlet.processRequest(FrameworkPortlet.java:496)
    	at org.springframework.web.portlet.FrameworkPortlet.doDispatch(FrameworkPortlet.java:453)
    	at javax.portlet.GenericPortlet.render(GenericPortlet.java:175)
    	at org.apache.pluto.core.PortletServlet.dispatch(PortletServlet.java:208)
    	at org.apache.pluto.core.PortletServlet.doGet(PortletServlet.java:139)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
    	at 	
    [snip]
    Caused by: java.text.ParseException: Unparseable date: "bogus"
    	at java.text.DateFormat.parse(DateFormat.java:335)
    	at org.springframework.binding.convert.converters.StringToDate.toObject(StringToDate.java:90)
    	... more
    
    Nested Exception is org.springframework.webflow.execution.FlowExecutionException: Exception thrown in state 'enterBookingDetails' of flow 'booking'
    	at org.springframework.webflow.engine.impl.FlowExecutionImpl.wrap(FlowExecutionImpl.java:568)
    	at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:267)
    	at org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:153)
    	at org.springframework.webflow.mvc.portlet.FlowHandlerAdapter.handleAction(FlowHandlerAdapter.java:141)
    	at org.springframework.web.portlet.DispatcherPortlet.doActionService(DispatcherPortlet.java:694)
    	at org.springframework.web.portlet.FrameworkPortlet.processRequest(FrameworkPortlet.java:480)
    	at org.springframework.web.portlet.FrameworkPortlet.processAction(FrameworkPortlet.java:462)
    	at org.apache.pluto.core.PortletServlet.dispatch(PortletServlet.java:218)
    	at org.apache.pluto.core.PortletServlet.doPost(PortletServlet.java:145)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:709)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
    	at 
    [snip]
    Caused by: org.springframework.binding.convert.ConversionExecutionException: Unable to convert value bogus from type 'java.lang.String' to type 'java.util.Date; reason = 'Invalid format for value 'bogus'; the expected format was 'yyyy-MM-dd''
    	at org.springframework.binding.convert.service.StaticConversionExecutor.execute(StaticConversionExecutor.java:101)
    	at org.springframework.binding.expression.el.ELExpression.setValue(ELExpression.java:92)
    	at org.springframework.binding.mapping.impl.DefaultMapping.map(DefaultMapping.java:133)
    	at org.springframework.binding.mapping.impl.DefaultMapper.map(DefaultMapper.java:68)
    	at org.springframework.webflow.mvc.view.AbstractMvcView.bind(AbstractMvcView.java:296)
    	at org.springframework.webflow.mvc.view.AbstractMvcView.processUserEvent(AbstractMvcView.java:190)
    	at org.springframework.webflow.engine.ViewState.resume(ViewState.java:199)
    	at org.springframework.webflow.engine.Flow.resume(Flow.java:551)
    	at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:263)
    	... 37 more
    Caused by: org.springframework.binding.convert.converters.InvalidFormatException: Invalid format for value 'bogus'; the expected format was 'yyyy-MM-dd'
    	at org.springframework.binding.convert.converters.StringToDate.toObject(StringToDate.java:92)
    	at org.springframework.binding.convert.converters.StringToObject.convertSourceToTargetClass(StringToObject.java:37)
    	at org.springframework.binding.convert.service.StaticConversionExecutor.execute(StaticConversionExecutor.java:99)
    	... 45 more
    Caused by: java.text.ParseException: Unparseable date: "bogus"
    	at java.text.DateFormat.parse(DateFormat.java:335)
    	at org.springframework.binding.convert.converters.StringToDate.toObject(StringToDate.java:90)
    	... 47 more

Posting Permissions

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