Struts 2 s:如果存在任何动作标记,则动作标记不会呈现动作错误

时间:2010-10-28 17:18:43

标签: java struts2 validation

例如,我有以下内容:

struts.xml中:

<action name="personForm">
    <result>/jsp/personForm.jsp</result>
</action>
<action name="savePerson">
    <result>/jsp/successSave.jsp</result>
    <result name="input">/jsp/personForm.jsp</result>
</action>
<action name="countries">
    <result>/jsp/countries.jsp</result>
</action>

personForm.jsp:

<%@ taglib prefix="s" uri="/struts-tags" %>
<s:form action="savePerson">
        <s:textfield name="firstName" label="First Name" />
        <s:textfield name="lastName" label="Last Name" />
        <s:action name="countries" executeResult="true" />
        <s:submit />
</s:form>

CountriesAction.java:

public class CountriesAction extends ActionSupport {
    public String execute() {
        countries = getCountries();
        return SUCCESS;
    }

    private Map<String, String> getCountries() {
            ...
    }

    private Map<String, String> countries;  
}

countries.jsp:

    <%@ taglib prefix="s" uri="/struts-tags" %>
    <s:select name="countryId" label="Countries" list="countries" 
        headerKey="-1" headerValue="Please select the country ..."/>

SavePerson.action

public class SavePerson extends ActionSupport {

    public void validate() {
        if (firstName == "") {
            addFieldError(firstName, "First Name is required.");
        }

        if (lastName == "") {
            addFieldError(lastName, "Last Name is required.");
        }

        if (countryId == "-1") {
            addFieldError(countryId, "Country is required.");
        }

    }

    public String execute() {
        //get the properties and save the person...
        return SUCCESS;
    }

    private String firstName;
    private String lastName;
    private String countryId;

    //corresponding setters and getters..
}

当我提交表单并发生验证错误时,例如假设我们没有填写任何数据,因此输入字段'firstName'和'lastName'将在它们旁边显示相应的消息。但是国家选择列表的情况并非如此,即使存在无法显示的操作错误。

我相信这是因为作为SavePerson的父操作是添加错误的人(addFieldErrors),但是当调用其他操作States(填充列表的那个)时,那些错误在该上下文中不可用,因为如果我在该Action中调用hasErrors()它将是“false”,所以当输入被呈现并检查是否有任何错误以呈现消息时将调用hasErrors并返回false,因此不会呈现错误消息。 / p>

这种调用另一个动作只是为了渲染另一个输入控件的方法是Struts 2 FAQS告诉你的方法之一: http://struts.apache.org/2.2.1/docs/how-do-we-repopulate-controls-when-validation-fails.html

那么如何对这些操作进行控制会从其父操作中呈现操作错误。

有什么想法吗?

提前谢谢

1 个答案:

答案 0 :(得分:2)

我通过将调用者操作中的错误引用设置为调用的操作来解决此问题。这是作为拦截器实现的:

public class CopyErrorsInterceptor extends AbstractInterceptor {


    public String intercept(ActionInvocation invocation) throws Exception {
        ValueStack stack =  invocation.getStack();
        CompoundRoot root = stack.getRoot();
        Action currentAction = (Action) invocation.getAction();
        if (root.size() > 1 && isValidationAware(currentAction)) {
            Action callerAction = getFirstActionBelow(root, currentAction);
            if (callerAction != null && isValidationAware(callerAction)) {
                ValidationAware currentActionVal = (ValidationAware) currentAction;
                ValidationAware callerActionVal = (ValidationAware) callerAction;

                //Copy the errors to the chained action.
                currentActionVal.setActionErrors(callerActionVal.getActionErrors());
                currentActionVal.setFieldErrors(callerActionVal.getFieldErrors());
            }
        }

        return invocation.invoke();
    }

    /**
     * Gets the first action below the passed action.
     * @param root the stack to find the action
     * @param current is the current action.
     * @return
     */
    private Action getFirstActionBelow(CompoundRoot root, Action current) {
        boolean foundCurrentAction = false;
        for(Object obj : root) {
            if (obj == current) {
                foundCurrentAction = true;
            } else {
                if (obj instanceof Action && foundCurrentAction) {
                    return (Action) obj;
                }
            }
        }
        return null;
    }

    private boolean isValidationAware(Action action) {
        return action instanceof ValidationAware;
    }

}

必须声明我自己的堆栈,它扩展了struts-default:

<interceptors>
            <interceptor name="copyErrors" class="com.afirme.casa.interceptor.CopyErrorsInterceptor"/>

            <interceptor-stack name="defaultPlusErrors">
                <interceptor-ref name="copyErrors"/>
                <interceptor-ref name="defaultStack">
                    <param name="workflow.excludeMethods">
                        input,back,cancel,execute
                    </param>
                </interceptor-ref>
            </interceptor-stack>
        </interceptors>

对于其他动作(在这种情况下通过动作标签)将要引用的动作必须引用这个新的拦截器堆栈。例如:

<action name="example" class="ExampleAction">
            <interceptor-ref name="defaultPlusErrors"/>
            <result>/jsp/example.jsp</result>
        </action>

希望这有帮助,

Alfredo O