Struts2 INPUT result: how does it work? How are conversion / validation errors handled?
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.
Conversion and validation issue in Struts2
Put the model initialization in the declaration, not in the getter:
private Product product = new Product();
@Override
public Product getModel() {
return product;
}
How does the validate() method in Struts 2 get the result input?
The validate()
method itself returns nothing (void
). It's invoked by validation
interceptor.
But after this interceptor usually is going workflow
interceptor. This interceptor is responsible to return INPUT
result if the action hasErrors()
.
Struts2 conversion warning
If you encountered this, there is no solution not to display this WARN on console or log:
Please read:
http://www.mail-archive.com/user@struts.apache.org/msg90542.html
Struts2 validation is not working properly, validation error messages not shown
After having read how the INPUT result works and having abandoned the ModelDriven design pattern that adds nothing to your programming experience except problems, that might easily hide themselves in the middle of the Interceptor Stack, note that:
redirect
is the result to use when redirecting to external URLs or non-Action URLs, whileredirectAction
should be used when redirecting to an Action;- when redirecting, a new request is created, and hence the old parameters (including action errors and messages and field errors) are lost.
To prevent that, if you want to keep using the PRG pattern (and hence the redirection), you can use the MessageStore Interceptor that will store and retrieve the messages for you across the redirections; just define an interceptor stack that contains it, and it will solve your problem.
Or do it once like in the example from the documentation.
struts2 making me go mad
No result defined for action
com.comviva.im.ui.action.sysadmin.CUGAction and result input
This means that you are lacking the mapping for the input
result for this Action in your Struts.xml
The standard workflow
JSP -> Interceptor Stack -> Action
is breaking BEFORE reaching the Action, most likely by the ConversionError Interceptor (eg. sending a String to an Integer, for example), and it is returning back without even reaching the Action.
Read more on Struts2 INPUT result: how does it work? How are conversion / validation errors handled?
When trying to come back, it can't find where to go.
So you must define an input
result type:
<action name="CUGAction" class="com.comviva.im.ui.action.sysadmin.CUGAction">
<result name="success">/your.jsp</result>
<result name="input">/your.jsp</result>
</action>
Then print out the error in your JSP with <s:fielderror />
and with <s:actionerrors />
, and try to figure out what is happening.
Form validation in Struts2
INPUT
is one of the predefined results made available by Struts2;
Basically, if your Action sends parameters that are incorrect (conversion errors, like if you send a "abc"
to an Integer
Action variable), or that don't pass the validation, the Workflow Interceptor return the INPUT
result and follow the path specified in the struts configuration for that Action.
Your problem is that you have not defined any INPUT
result for your Actions, while you always should.
You can also set a global input result as a fallback, but be careful with that... generally, the page you want to go in case of an INPUT
is the same from where the request has been sent.
In case of an INPUT
result, your Action method (eg. execute()
) is not executed, so if you load common data in that method, like select boxes content, it won't be available anymore.
Read this answers to fully understand what this implies and how to make it work:
How do we repopulate controls when validation fails
Detailed Workflow of the INPUT result processing
Change Struts Validator default messages
That is not a validation error message, it is a conversion error message.
You can override the default conversion error message up to each single object, by creating an entry for it in the global .properties file, as described in Struts 2 documentation, Type Conversion Errors Handling:
By default, all conversion errors are reported using the generic i18n
key xwork.default.invalid.fieldvalue, which you can override (the
default text is Invalid field value for field "xxx", where xxx is the
field name) in your global i18n resource bundle.However, sometimes you may wish to override this message on a
per-field basis. You can do this by adding an i18n key associated with
just your action (Action.properties) using the pattern
invalid.fieldvalue.xxx, where xxx is the field name.
If you are interested in understanding how it works in a deeper way, read the Short Story about Validation, Conversion and Friends.
Related Topics
Implementing Two Interfaces in a Class with Same Method. Which Interface Method Is Overridden
Java Maximum Memory on Windows Xp
Obtaining a Powerset of a Set in Java
How to Get a List of Dates Between Two Dates in Java
Find Java Classes Implementing an Interface
How to Extract a Substring Using Regex
Use Mockito to Mock Some Methods But Not Others
Java - Best Approach to Parse Huge (Extra Large) JSON File
Accessing Members of Items in a JSONarray with Java
How to Iterate Through the Files in a Directory and It's Sub-Directories in Java
Is There a Performance Difference Between a for Loop and a For-Each Loop
How Does the String Class Override the + Operator
How to Supply Value to an Annotation from a Constant Java
Connecting to Remote Url Which Requires Authentication Using Java