|
From: Ben A. <ben...@ac...> - 2004-10-04 22:10:53
|
I'm using SimpleFormController to process a form containing a parameter that is converted to a Long value in the form backing object. The Long has a check digit and thus there are both number format exceptions as well as check digit related exceptions possible. By default such errors will cause the following messages to be displayed to the user in the errors list: Failed to convert property value of type [java.lang.String] to required type [java.lang.Long] for property 'luhn'; nested exception is java.lang.IllegalArgumentException: Cannot parse number: Unparseable number: "aString" or Property 'luhn' threw exception; nested exception is java.lang.IllegalArgumentException: Business key must be a valid Luhn identifier Neither response is suitable for display to users. I would rather replace these with my own more user-friendly message. One approach discussed at http://forum.springframework.org/viewtopic.php?t=1098 (last post by ojolly) is for the PropertyEditor to set the object to null, then detect something was actually entered in the request parameter in onBindAndValidate and then add a friendly error. That's OK, but then you lose whatever the user actually typed in as the PropertyEditor set it to null. This is unacceptable as quite often the data entered will be close to what it should have been (eg an unwanted fractional portion for an integer, a mis-typed digit for a checksum-based number, a property that is simply too long etc). I ended up writing a MyBindException which subclasses BindException and provides these two methods: public void removeError(ObjectError error) { this.errors.remove(error); } public void removeErrors(List objectErrors) { Iterator iter = objectErrors.iterator(); while (iter.hasNext()) { ObjectError objectError = (ObjectError) iter.next(); removeError(objectError); } } My controller then does this: protected void onBindAndValidate(HttpServletRequest request, Object command, BindException errors) throws Exception { Person person = (Person) command; if (errors.hasFieldErrors("luhn")) { // Cast is safe as createBinder caused MyBindException to be used MyBindException myErrors = (MyBindException) errors; errors.rejectValue("luhn", "LUHN_INVALID", "A valid Luhn is required."); } .... This works great. It consumes the unwanted, uninformative user feedback and replaces it. It also avoids needing to look at HttpServetRequest parameters. The problem is to achieve this I've needed to write MyBindException, copy nine methods to it (as the errors List is private in the BindException superclass), write a MyServletRequestDataBinder to set it up, and then override createBinder in the controller to use MyServletRequestDataBinder. It seems a lot of work, so I was hoping we could add the above two methods to BindException? Alternatively, at least making the errors List protected in BindException will save the ugly code duplication. Alternatively, if there's another way entirely of achieving this, I'd be grateful if someone would let me know. Best regards Ben |
|
From: Ben A. <ben...@ac...> - 2004-10-04 22:13:53
|
Ben Alex wrote:
> My controller then does this:
>
> protected void onBindAndValidate(HttpServletRequest request,
> Object command, BindException errors) throws Exception {
> Person person = (Person) command;
> if (errors.hasFieldErrors("luhn")) {
> // Cast is safe as createBinder caused MyBindException to
> be used
> MyBindException myErrors = (MyBindException) errors;
> errors.rejectValue("luhn", "LUHN_INVALID", "A valid Luhn is
> required.");
> } ....
>
>
Ooops, that code fragment forgot a line:
// Cast is safe as createBinder caused MyBindException to be use
MyBindException myErrors = (MyBindException) errors;
myErrors.removeErrors(myErrors.getFieldErrors("luhn"));
// *********
errors.rejectValue("luhn", "LUHN_INVALID", "2 A valid Luhn is
required.");
|
|
From: Dmitriy K. <dko...@ru...> - 2004-10-04 23:04:17
|
Ben,
you don't need to do anything special here. Simply provide a custom
message in "messages.properties" found in the classpath for a
"typeMismatch.{property_name} key and Spring will take care of the rest i.e.
typeMismatch.luhn=A valid Luhn is required
Also don't forget to configure the MessageSource bean in your app
context with base name of "messages":
<!-- Message source for this context, loaded from localized
"messages_xx" files -->
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename"><value>messages</value></property>
</bean>
Take a look at Petclinic's example.
Cheers,
Dmitriy.
Ben Alex wrote:
> I'm using SimpleFormController to process a form containing a
> parameter that is converted to a Long value in the form backing
> object. The Long has a check digit and thus there are both number
> format exceptions as well as check digit related exceptions possible.
> By default such errors will cause the following messages to be
> displayed to the user in the errors list:
>
> Failed to convert property value of type [java.lang.String] to
> required type [java.lang.Long] for property 'luhn'; nested exception
> is java.lang.IllegalArgumentException: Cannot parse number:
> Unparseable number: "aString"
>
> or
>
> Property 'luhn' threw exception; nested exception is
> java.lang.IllegalArgumentException: Business key must be a valid Luhn
> identifier
>
> Neither response is suitable for display to users. I would rather
> replace these with my own more user-friendly message. One approach
> discussed at http://forum.springframework.org/viewtopic.php?t=1098
> (last post by ojolly) is for the PropertyEditor to set the object to
> null, then detect something was actually entered in the request
> parameter in onBindAndValidate and then add a friendly error. That's
> OK, but then you lose whatever the user actually typed in as the
> PropertyEditor set it to null. This is unacceptable as quite often the
> data entered will be close to what it should have been (eg an unwanted
> fractional portion for an integer, a mis-typed digit for a
> checksum-based number, a property that is simply too long etc).
>
> I ended up writing a MyBindException which subclasses BindException
> and provides these two methods:
>
> public void removeError(ObjectError error) {
> this.errors.remove(error);
> }
> public void removeErrors(List objectErrors) {
> Iterator iter = objectErrors.iterator();
> while (iter.hasNext()) {
> ObjectError objectError = (ObjectError) iter.next();
> removeError(objectError);
> }
> }
>
> My controller then does this:
>
> protected void onBindAndValidate(HttpServletRequest request,
> Object command, BindException errors) throws Exception {
> Person person = (Person) command;
> if (errors.hasFieldErrors("luhn")) {
> // Cast is safe as createBinder caused MyBindException to
> be used
> MyBindException myErrors = (MyBindException) errors;
> errors.rejectValue("luhn", "LUHN_INVALID", "A valid Luhn is
> required.");
> } ....
>
> This works great. It consumes the unwanted, uninformative user
> feedback and replaces it. It also avoids needing to look at
> HttpServetRequest parameters. The problem is to achieve this I've
> needed to write MyBindException, copy nine methods to it (as the
> errors List is private in the BindException superclass), write a
> MyServletRequestDataBinder to set it up, and then override
> createBinder in the controller to use MyServletRequestDataBinder. It
> seems a lot of work, so I was hoping we could add the above two
> methods to BindException? Alternatively, at least making the errors
> List protected in BindException will save the ugly code duplication.
>
> Alternatively, if there's another way entirely of achieving this, I'd
> be grateful if someone would let me know.
>
> Best regards
> Ben
>
>
>
> -------------------------------------------------------
> This SF.net email is sponsored by: IT Product Guide on ITManagersJournal
> Use IT products in your business? Tell us what you think of them. Give us
> Your Opinions, Get Free ThinkGeek Gift Certificates! Click to find out
> more
> http://productguide.itmanagersjournal.com/guidepromo.tmpl
> _______________________________________________
> Springframework-developer mailing list
> Spr...@li...
> https://lists.sourceforge.net/lists/listinfo/springframework-developer
|
|
From: Ben A. <ben...@ac...> - 2004-10-04 23:38:55
|
Dmitriy Kopylenko wrote:
> Ben,
>
> you don't need to do anything special here. Simply provide a custom
> message in "messages.properties" found in the classpath for a
> "typeMismatch.{property_name} key and Spring will take care of the
> rest i.e.
>
> typeMismatch.luhn=A valid Luhn is required
>
>
Hi Dmitriy
Thanks for the info - that's a lot nicer.
For the benefit of the archives, this is covered in the JavaDocs of
org.springframework.validation.DefaultMessageCodesResolver.
Cheers
Ben
|
|
From: Colin S. <col...@ex...> - 2004-10-04 23:51:36
|
Unless I'm missing something, looks like the docs don't really get into
this area. I've updated the documentation TODO Jira entry with the
following note:
"""
There seems to be a general hole in the MVC docs w/regards to data
binding of form params to command objects, in terms of real details on
the mechanism, details for the format coming in, and details on how
exceptions and error codes (typeMismatch, etc.) can get mapped to
messages that get pulled out of the 'messages' bean, so users don't see
raw exceptions. Some of thee details may currently be obtained by
looking at some JavaDocs or source for classes like
BaseCommandController, DefaultMessageCodeResolver, DataBinder,
TypeMismatchException, and MethodInvocationException.
"""
Dmitriy Kopylenko wrote:
> Ben,
>
> you don't need to do anything special here. Simply provide a custom
> message in "messages.properties" found in the classpath for a
> "typeMismatch.{property_name} key and Spring will take care of the
> rest i.e.
>
> typeMismatch.luhn=A valid Luhn is required
>
> Also don't forget to configure the MessageSource bean in your app
> context with base name of "messages":
>
> <!-- Message source for this context, loaded from localized
> "messages_xx" files -->
> <bean id="messageSource"
> class="org.springframework.context.support.ResourceBundleMessageSource">
> <property name="basename"><value>messages</value></property>
> </bean>
>
> Take a look at Petclinic's example.
>
> Cheers,
> Dmitriy.
>
>
> Ben Alex wrote:
>
>> I'm using SimpleFormController to process a form containing a
>> parameter that is converted to a Long value in the form backing
>> object. The Long has a check digit and thus there are both number
>> format exceptions as well as check digit related exceptions possible.
>> By default such errors will cause the following messages to be
>> displayed to the user in the errors list:
>>
>> Failed to convert property value of type [java.lang.String] to
>> required type [java.lang.Long] for property 'luhn'; nested exception
>> is java.lang.IllegalArgumentException: Cannot parse number:
>> Unparseable number: "aString"
>>
>> or
>>
>> Property 'luhn' threw exception; nested exception is
>> java.lang.IllegalArgumentException: Business key must be a valid Luhn
>> identifier
>>
>> Neither response is suitable for display to users. I would rather
>> replace these with my own more user-friendly message. One approach
>> discussed at http://forum.springframework.org/viewtopic.php?t=1098
>> (last post by ojolly) is for the PropertyEditor to set the object to
>> null, then detect something was actually entered in the request
>> parameter in onBindAndValidate and then add a friendly error. That's
>> OK, but then you lose whatever the user actually typed in as the
>> PropertyEditor set it to null. This is unacceptable as quite often
>> the data entered will be close to what it should have been (eg an
>> unwanted fractional portion for an integer, a mis-typed digit for a
>> checksum-based number, a property that is simply too long etc).
>>
>> I ended up writing a MyBindException which subclasses BindException
>> and provides these two methods:
>>
>> public void removeError(ObjectError error) {
>> this.errors.remove(error);
>> }
>> public void removeErrors(List objectErrors) {
>> Iterator iter = objectErrors.iterator();
>> while (iter.hasNext()) {
>> ObjectError objectError = (ObjectError) iter.next();
>> removeError(objectError);
>> }
>> }
>>
>> My controller then does this:
>>
>> protected void onBindAndValidate(HttpServletRequest request,
>> Object command, BindException errors) throws Exception {
>> Person person = (Person) command;
>> if (errors.hasFieldErrors("luhn")) {
>> // Cast is safe as createBinder caused MyBindException to
>> be used
>> MyBindException myErrors = (MyBindException) errors;
>> errors.rejectValue("luhn", "LUHN_INVALID", "A valid Luhn
>> is required.");
>> } ....
>>
>> This works great. It consumes the unwanted, uninformative user
>> feedback and replaces it. It also avoids needing to look at
>> HttpServetRequest parameters. The problem is to achieve this I've
>> needed to write MyBindException, copy nine methods to it (as the
>> errors List is private in the BindException superclass), write a
>> MyServletRequestDataBinder to set it up, and then override
>> createBinder in the controller to use MyServletRequestDataBinder. It
>> seems a lot of work, so I was hoping we could add the above two
>> methods to BindException? Alternatively, at least making the errors
>> List protected in BindException will save the ugly code duplication.
>>
>> Alternatively, if there's another way entirely of achieving this, I'd
>> be grateful if someone would let me know.
>>
>> Best regards
>> Ben
>>
>>
>>
>> -------------------------------------------------------
>> This SF.net email is sponsored by: IT Product Guide on ITManagersJournal
>> Use IT products in your business? Tell us what you think of them.
>> Give us
>> Your Opinions, Get Free ThinkGeek Gift Certificates! Click to find
>> out more
>> http://productguide.itmanagersjournal.com/guidepromo.tmpl
>> _______________________________________________
>> Springframework-developer mailing list
>> Spr...@li...
>> https://lists.sourceforge.net/lists/listinfo/springframework-developer
>
|