【问题标题】:struts2 validation using message store interceptorstruts2 使用消息存储拦截器进行验证
【发布时间】:2015-04-15 01:11:07
【问题描述】:

考虑一下:

  1. 用户点击链接
  2. 请求转到 DisplayLoginAction
  3. 显示Login.jsp
  4. 用户输入他的凭据
  5. 表单被映射到ValidateLoginAction
  6. ValidateLoginAction 中的验证失败
  7. ValidateLoginAction 存储错误并返回“输入”
  8. 重定向到 DisplayLoginAction..
  9. DisplayLoginAction 从拦截器中检索错误并将其显示给登录表单上方的用户
  10. 用户刷新页面
  11. 错误消失了

我应该如何保存页面刷新时的错误?

<action name="displayLoginPage" class="DisplayLoginAction">
   <interceptor-ref name="store">
    <param name="operationMode">RETRIEVE</param>
    </interceptor-ref>        
   <interceptor-ref name="customStack"/> 
     <result name="success">Login.jsp</result>
     <result name="input">Login.jsp</result>  
</action> 

<action name="validateloginForm" class="ValidateLoginAction">
   <interceptor-ref name="store">
    <param name="operationMode">STORE</param>
   </interceptor-ref>
   <interceptor-ref name="customStack"/> 
     <result name="input" type="redirectAction">displayLoginPage</result>
     <result name="success">LoginConfirmation.jsp</result>
</action>

【问题讨论】:

  • 消息存储拦截器允许在验证失败时将消息存储在会话中,以便以后在另一个操作中检索它们。
  • 是的,当我在操作 B 中存储错误并使用拦截器在操作 A 中检索它们时,它可以正常工作,但是如果我通过刷新页面重新加载操作 A,错误会再次消失

标签: validation jsp struts2 validationerror


【解决方案1】:
  1. 用户刷新页面

  2. 错误消失了

我应该如何保存页面刷新时的错误?

这就是MessageStoreInterceptor 的工作方式。它实际上是一个特性,而不是一个错误。

页面刷新是用户触发的动作,这意味着可以假设他已经阅读了上一次操作(登录尝试)的结果,除非他是闭着眼睛按F5。

您应该希望消息在第一次阅读后过期。

考虑一个有很多非ajax操作的页面,比如依赖于其他的组合框等...如果错误消息是持久的,它会在每次提交操作后弹出,而不涉及到另一个页面。

你不想这样。您应该希望在 那个 操作之后收到一条消息,指出一个操作出错(或正确)。如果用户随后继续执行其他操作,例如刷新,并且这些操作没有出错(或处于特定的成功状态),则不应显示任何消息。

之后,何时从会话中删除持久消息也存在问题,因为否则您将在下一个操作中使用RETRIEVEAUTOMATIC 操作模式获取该消息。然后,您可以(但不应该)自定义 MessageStore 拦截器,或者自己在会话中写入/读取/删除消息,基本上是在重新发明轮子。甚至放两个 MessageStore 拦截器,一个 RETRIEVE 和一个 STORE 用于 displayLoginPage Action,得到刚才提到的陷阱。

您还使用PRG pattern (Post/Redirect/Get),以避免在刷新页面时重新发送数据。同样,您应该避免重复阅读相同的消息两次。

要了解具体是如何工作的,您可以查看MessageStore Interceptor source code,这很简单:

  • 在调用之前,如果 Action 是 ValidationAware 并且 operationModeRETRIEVEAUTOMATIC
    1. 阅读 actionMessagesactionErrorsfieldErrors 来自会话;
    2. 合并它们与当前操作的actionMessagesactionErrorsfieldErrors
    3. 从会话中删除 actionMessagesactionErrorsfieldErrors
/**
 * Handle the retrieving of field errors / action messages / field errors, which is
 * done before action invocation, and the <code>operationMode</code> is 'RETRIEVE'.
 *
 * @param invocation
 * @throws Exception
 */
protected void before(ActionInvocation invocation) throws Exception {
    String reqOperationMode = getRequestOperationMode(invocation);

    if (RETRIEVE_MODE.equalsIgnoreCase(reqOperationMode) ||
            RETRIEVE_MODE.equalsIgnoreCase(operationMode) ||
            AUTOMATIC_MODE.equalsIgnoreCase(operationMode)) {

        Object action = invocation.getAction();
        if (action instanceof ValidationAware) {
            // retrieve error / message from session
            Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION);

            if (session == null) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Session is not open, no errors / messages could be retrieve for action ["+action+"]");
                }
                return;
            }

            ValidationAware validationAwareAction = (ValidationAware) action;

            if (LOG.isDebugEnabled()) {
                LOG.debug("retrieve error / message from session to populate into action ["+action+"]");
            }

            Collection actionErrors = (Collection) session.get(actionErrorsSessionKey);
            Collection actionMessages = (Collection) session.get(actionMessagesSessionKey);
            Map fieldErrors = (Map) session.get(fieldErrorsSessionKey);

            if (actionErrors != null && actionErrors.size() > 0) {
                Collection mergedActionErrors = mergeCollection(validationAwareAction.getActionErrors(), actionErrors);
                validationAwareAction.setActionErrors(mergedActionErrors);
            }

            if (actionMessages != null && actionMessages.size() > 0) {
                Collection mergedActionMessages = mergeCollection(validationAwareAction.getActionMessages(), actionMessages);
                validationAwareAction.setActionMessages(mergedActionMessages);
            }

            if (fieldErrors != null && fieldErrors.size() > 0) {
                Map mergedFieldErrors = mergeMap(validationAwareAction.getFieldErrors(), fieldErrors);
                validationAwareAction.setFieldErrors(mergedFieldErrors);
            }
            session.remove(actionErrorsSessionKey);
            session.remove(actionMessagesSessionKey);
            session.remove(fieldErrorsSessionKey);
        }
    }
}
  • 调用后,如果 Action 为 ValidationAwareoperationModeSTORE 或(operationModeAUTOMATIC 且 Result 的类型为 redirectAction):
    1. 阅读 actionMessages, actionErrors, fieldErrors 行动;
    2. 存储 actionMessagesactionErrorsfieldErrors 在会话中。
/**
 * Handle the storing of field errors / action messages / field errors, which is
 * done after action invocation, and the <code>operationMode</code> is in 'STORE'.
 *
 * @param invocation
 * @param result
 * @throws Exception
 */
protected void after(ActionInvocation invocation, String result) throws Exception {

    String reqOperationMode = getRequestOperationMode(invocation);
    boolean isRedirect = invocation.getResult() instanceof ServletRedirectResult;
    if (STORE_MODE.equalsIgnoreCase(reqOperationMode) ||
            STORE_MODE.equalsIgnoreCase(operationMode) ||
            (AUTOMATIC_MODE.equalsIgnoreCase(operationMode) && isRedirect)) {

        Object action = invocation.getAction();
        if (action instanceof ValidationAware) {
            // store error / messages into session
            Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION);

            if (session == null) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Could not store action ["+action+"] error/messages into session, because session hasn't been opened yet.");
                }
                return;
            }

            if (LOG.isDebugEnabled()) {
                LOG.debug("store action ["+action+"] error/messages into session ");
            }

            ValidationAware validationAwareAction = (ValidationAware) action;
            session.put(actionErrorsSessionKey, validationAwareAction.getActionErrors());
            session.put(actionMessagesSessionKey, validationAwareAction.getActionMessages());
            session.put(fieldErrorsSessionKey, validationAwareAction.getFieldErrors());
        }
        else if(LOG.isDebugEnabled()) {
        LOG.debug("Action ["+action+"] is not ValidationAware, no message / error that are storeable");
        }
    }
}

注1:登录操作也是不言自明的:如果登录后再次登陆登录页面,只能表示登录失败......顺便说一句以上解释适用于每个页面/功能
注意 2:有些网站生成的消息会在 X 秒后自动过期,而不关心用户是否阅读过它们...可用性,恕我直言。

【讨论】:

  • 感谢您提供非常彻底的回答,我同意这是一个功能而不是错误,但是我只是在这样做之后才意识到这个应用程序的“正确”方式,我正在转换为struts 2,在其以前的版本中,即使在页面重新加载后也会显示错误,它的做法是重新提交带有错误数据的表单,这将导致重新显示错误
  • 没错,没有 PRG 没有派对
  • 显然,这不是我的困境的结束,在这个网络应用程序中,用户应该能够更改每个页面上的语言,包括已经显示验证错误的登录页面,除非我这样做重新发布我怎样才能再次获得验证错误以便在不同的语言环境中显示它们
猜你喜欢
  • 1970-01-01
  • 2012-06-18
  • 2010-12-21
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-31
相关资源
最近更新 更多