Struts 2 s:action 标签不会呈现操作错误(如果存在)

发布于 2024-09-29 04:52:07 字数 2748 浏览 1 评论 0原文

例如,我有以下内容:

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>

CountryAction.java:

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

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

    private Map<String, String> countries;  
}

country.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),但是当调用其他操作 Country(填充列表的操作)时,这些错误在该上下文中不可用,因为如果我调用该 Action 中的 hasErrors() 将为“false”,因此当呈现输入并检查是否存在任何错误以呈现消息时,将调用 hasErrors 并返回 false,因此不会呈现任何错误消息。

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

那么我怎样才能使这些操作上的控件从其父操作中呈现操作错误。

有什么想法吗?

先感谢您

For example I have the following:

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..
}

When I submit the form and a validation error occurs for example let's say we didn't fill in any data so the input fields 'firstName' and 'lastName' will have their corresponding message next to them. But that's not the case for the country select list, even though there are action errors those won't display.

I believe this happens because the parent action which is SavePerson is the one who added the errors (addFieldErrors) but when the other action Countries (the one that populates the list) is called then those errors are not available in that context because if I call hasErrors() within that Action it will be "false" so when the input gets rendered and check if there are any errors in order to render the message will call hasErrors and will return false so no errors messages will be rendered.

This approach of calling another action just to render another input controls is one of the ways that Struts 2 FAQS tell to do that:
http://struts.apache.org/2.2.1/docs/how-do-we-repopulate-controls-when-validation-fails.html

So how can I make those controls on those actions render the action errors from its parent action.

Any thoughts?

Thank you in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

羅雙樹 2024-10-06 04:52:07

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

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

I resolved this by setting the reference of the errors from the caller action to the invoked action. This was implemented as an interceptor:

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;
    }

}

Had to declare my own stack which extends 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>

And to the actions that are going to be inovked by other actions (in this case through the action tags) have to reference this new stack of interceptors. Example:

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

Hope this helps,

Alfredo O

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文