Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
152 views
in Technique[技术] by (71.8m points)

java - 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; shouldn't that exception (that is NumberFormatException) be pushed into Value Stack ? Why does it not show NumberFormatException and show the result even though result should not be printed instead ?

Side Question

Whenever I add an alphabet in the form, it changed to zero...? Why so ?

index.jsp

<%@ taglib uri="/struts-tags" prefix="s"%>
<s:form action="divide">
    <s:textfield name="number1" label="number1"/>
    <s:textfield name="number2" label="number2"/>
    <s:submit value="divide"/>
</s:form>

divide.java

package actions;

public class divide {
    int number1,number2,result;
    public String execute() throws Exception
    {
        result=number1/number2;
        return "success";
    }
    public int getNumber1() {
        return number1;
    }
    public void setNumber1(int number1) {
        this.number1 = number1;
    }
    public int getNumber2() {
        return number2;
    }
    public void setNumber2(int number2) {
        this.number2 = number2;
    }
    public int getResult() {
        return result;
    }


}

result.jsp

<%@taglib uri="/struts-tags" prefix="s" %>
<b>
    the result of division is
    <s:property value="result"/>
</b>
<jsp:include page="index.jsp"></jsp:include>

handler jsp

<%@taglib uri="/struts-tags" prefix="s"%>
<b>
    following exception occured during the processing
    <s:property value="exception"/>
</b>
<jsp:include page="index.jsp"/>

struts.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC 
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
    <struts>
        <package name="yo" extends="struts-default">
            <action name="divide" class="actions.divide">
                <exception-mapping result="error" exception="Exception"/>
                <result name="success">/result.jsp</result>
                <result name="error">/handler.jsp</result>
            </action>
        </package>
    </struts>
Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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:
    {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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...