Struts2 Input Result: How Does It Work? How Are Conversion/Validation Errors Handled

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

  1. The Parameters Interceptor tries to set the parameters. If an RuntimeException (like NumberFormatException) is caught and devMode is true, an error message is added to the Action 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);
    }
    }
    }
    }
  2. The Conversion Errors Interceptor checks if any conversion error happened: for each one found, it adds a Field 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).

  3. The Validation Interceptor performs all the validation requested (defined in XML, Annotations or through the validate() or validateXXX() methods of the Action), adding one or more error messages to the Field Errors for each field not passing one or more validation criteria.

  4. The Workflow Interceptor checks if there are Field 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, while redirectAction 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



Leave a reply



Submit