Struts2 INPUT result: how does it work? How are conversion / validation errors handled?
Solution 1:
Main Question:
The work flow should be like this ,if an string is entered other than a number,first it should pass through a exception interceptor,and when passing through param interceptor,while converting to int type,it wont be able to do it using Integer.parseInt and an exception would occur,that exception that is number format exception should be pushed into value stack?so why does it not show numberformatexception and show the result even though result should not be printed instead?
Concept
Struts 2 handles both conversion errors and validation errors automatically: it does not raise an Exception, because they're not blocking errors, but input errors, hence the best way to proceed is to notify the user that the input submitted was wrong, asking him for a new, valid input. To achieve this, an INPUT result is returned, while the Exception is ignored.
Detailed worflow
-
The
Parameters Interceptor
tries to set the parameters. If anRuntimeException
(likeNumberFormatException
) is caught anddevMode
istrue
, an error message is added to theAction Errors
, otherwise the exception is simply swallowed. From the source code:for (Map.Entry<String, Object> entry : acceptableParameters.entrySet()) { String name = entry.getKey(); Object value = entry.getValue(); try { newStack.setParameter(name, value); } catch (RuntimeException e) { if (devMode) { String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{ "Unexpected Exception caught setting '" + name + "' on '" + action.getClass() + ": " + e.getMessage() }); LOG.error(developerNotification); if (action instanceof ValidationAware) { ((ValidationAware) action).addActionMessage(developerNotification); } } } }
-
The
Conversion Errors Interceptor
checks if any conversion error happened: for each one found, it adds aField Error
; it also saves the original values such that any subsequent requests for that value return the original value rather than the value in the action. From the documentation:This interceptor adds any error found in the ActionContext's conversionErrors map as a field error (provided that the action implements ValidationAware). In addition, any field that contains a validation error has its original value saved such that any subsequent requests for that value return the original value rather than the value in the action. This is important because if the value "abc" is submitted and can't be converted to an int, we want to display the original string ("abc") again rather than the int value (likely 0, which would make very little sense to the user).
The
Validation Interceptor
performs all the validation requested (defined in XML, Annotations or through thevalidate()
orvalidateXXX()
methods of the Action), adding one or more error messages to theField Errors
for each field not passing one or more validation criteria.The
Workflow Interceptor
checks if there areField Errors
(both coming from conversion errors or validation errors). If no errors are found, it continues the chain to the next Interceptor. If one or more errors are found, it returns an INPUT result.
To ensure this mechanism works, you need to define this four Interceptors in the right order in your Custom Stack, if you are not using the Default Interceptors Stack (you don't need to do anything otherwise). From struts-default.xml
:
<!-- others interceptors here... -->
<interceptor-ref name="params">
<param name="excludeParams">^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<!-- ... others interceptors here -->
Side Question:
Whenever I add an alphabet in the form, it changed to zero...? Why so?
The original answer was: the framework has not been able to set a String
into an int
field when posting the request to the server, and when retrieving the value in the resulting page, it invokes the Getter of that variable; since you defined an int
and not an Integer
, and an int
can't be null, it will return the default value for an int
: 0.
But I wasn't remembering that Conversion Interceptor claims (read point n. 2) to save the original values, to provide them in subsequent future requests, in place of the Action values (that would be null, or 0). This is also mentioned in Type Conversion Error Handling:
Type conversion error handling provides a simple way to distinguish between an input validation problem and an input type conversion problem.
Any error that occurs during type conversion may or may not wish to be reported. For example, reporting that the input "abc" could not be converted to a number might be important. On the other hand, reporting that an empty string, "", cannot be converted to a number might not be important - especially in a web environment where it is hard to distinguish between a user not entering a value vs. entering a blank value.
...
Instead, I was remembering well the behavior described in your question.
So this case has already been handled... why it is not working then ?
The culprit, in my case (and probably your), was the value
attribute:
This will give you 0
when posting abc
:
<s:textfield name = "myIntField"
value = "%{getText('format.number',{myIntField})}" />
because a further conversion error occours.
This two cases instead work as described above, giving you abc
when posting abc
:
<s:textfield name = "myIntField" />
<s:textfield name = "myIntField"
value = "%{myIntField}" />
Conclusions
- Ensure the Interceptor Stack is correctly configured, and
- check carefully your code (that is most likely not the one posted here) to see what are you doing with your
value
attribute.
For test purposes, try removing the value
attribute at all at first, to see it working the right way, then start looking for the bug.