使用消息存储拦截器进行struts2验证 [英] struts2 validation using message store interceptor

查看:94
本文介绍了使用消息存储拦截器进行struts2验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑这一点:

  1. 用户点击链接
  2. 请求转到DisplayLoginAction
  3. 显示Login.jsp
  4. 用户输入其凭据
  5. 表单已映射到ValidateLoginAction
  6. 验证在ValidateLoginAction中失败
  7. ValidateLoginAction存储错误并返回输入"
  8. 重定向到DisplayLoginAction.
  9. DisplayLoginAction从拦截器中检索错误,并在登录表单上方将其显示给用户
  10. 用户刷新页面
  11. 错误消失了
  1. user clicks on a link
  2. request goes to DisplayLoginAction
  3. Login.jsp is displayed
  4. user enters his credentials
  5. form is mapped to ValidateLoginAction
  6. validation fails in ValidateLoginAction
  7. ValidateLoginAction stores the errors and returns "input"
  8. redirecting to DisplayLoginAction..
  9. DisplayLoginAction retrieves the errors from the interceptor and shows them to the user above the login form
  10. user refreshes the page
  11. The errors are gone

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

How should I save the errors on page refresh?

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

推荐答案

  1. 用户刷新页面

  1. user refreshes the page

错误消失了

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

How should I save the errors on page refresh?

这就是 MessageStoreInterceptor 的工作方式.它实际上是一项功能,而不是错误.

That is the way MessageStoreInterceptor works. It is actually a feature, not a bug.

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

Page refresh is an action triggered by the user, that means it can be assumed he has already read the result of the previous operation (the login attempt), unless he is pressing F5 with the eyes closed.

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

请考虑一个页面,其中包含许多非ajax操作,例如combobox取决于其他操作,等等.如果错误消息持续存在,则它将在每次不涉及进入另一页面的提交操作之后弹出.

Consider a page with a lot of non-ajax operations, like combobox depending on others, etc... If the error message is persistent, it would popup after each submit operation that does not involve going to another page.

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

You don't want that. You should want to get the message saying that one operation has gone wrong (or right) just after that operation. If the user then proceed with other operations, like a refresh, if those operations aren't going in error (or in a specific success state), no message should be shown.

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

After that, there's also the problem of when deleting from session a persistent message, because otherwise you would get that message in the next action with a RETRIEVE or AUTOMATIC operationMode. Then you could (but shouldn't) customize the MessageStore Interceptor, or writing / reading / deleting messages from and to the session on your own, basically reinventing the wheel. Or even put two MessageStore Interceptors, one RETRIEVE and one STORE for displayLoginPage Action, getting the pitfalls just mentioned.

您还使用了 PRG模式(发布/重定向/获取) ,以避免刷新页面时重新发送数据.同样,您应该避免重复读取相同的消息两次.

You are also using the PRG pattern (Post/Redirect/Get), to avoid re-sending the data when refreshing the page. At the same way, you should avoid re-reading the same messages twice.

要查看其具体工作方式,可以查看

To see how this specifically works, you can take a look at the MessageStore Interceptor source code, that is quite simple:

  • 在调用之前,如果Action为ValidationAwareoperationModeRETRIEVEAUTOMATIC:
    从会话中
  1. 阅读 actionMessagesactionErrorsfieldErrors
  2. 合并它们与当前操作的actionMessagesactionErrorsfieldErrors;
  3. 从会话中
  4. 删除 actionMessagesactionErrorsfieldErrors.
  1. read actionMessages, actionErrors, fieldErrors from session;
  2. merge them with current action's actionMessages, actionErrors, fieldErrors;
  3. remove actionMessages, actionErrors, fieldErrors from session.

/**
 * 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):

    • After Invocation, if Action is ValidationAware and operationMode is STORE or (operationMode is AUTOMATIC and Result is of type redirectAction):

      1. 阅读 actionMessagesactionErrorsfieldErrors
      2. 在会话中
      3. 存储 actionMessagesactionErrorsfieldErrors.
      1. read actionMessages, actionErrors, fieldErrors from action;
      2. store actionMessages, actionErrors, fieldErrors in the session.

    • /**
       * 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秒后自动过期,而不关心用户是否阅读过这些消息……并且 违反可用性,恕我直言.

      Note 1: The login operation is also self-explanatory: if after logging in you land on the login page again, it can only mean that the login failed... BTW the above explanation applies to every page/functionality
      Note 2: There are sites producing messages that expire automatically after X seconds, not caring about the user having read them or not... and that is against the usability, IMHO.

      这篇关于使用消息存储拦截器进行struts2验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆