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

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

问题描述

考虑一下:

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

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

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 操作,比如依赖于其他的组合框等.如果错误消息是持久的,它会在每次提交操作后弹出,而不涉及到另一个页面.

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.

之后,when 从会话中删除持久消息也存在问题,因为否则您将在下一个操作中使用 RETRIEVE 获取该消息>自动操作模式.然后,您可以(但不应该)自定义 MessageStore 拦截器,或者自己在会话中写入/读取/删除消息,基本上是在重新发明轮子.甚至放了两个MessageStore Interceptor,一个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 模式(Post/Redirect/Get), 避免刷新页面时重新发送数据.同样,您应该避免重复阅读相同的消息两次.

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.

要了解具体是如何工作的,您可以查看 MessageStore Interceptor源码,就是很简单:

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

  • 在调用之前,如果 Action 是 ValidationAware 并且 operationModeRETRIEVEAUTOMATIC:
  • Before Invocation, if Action is ValidationAware and operationMode is RETRIEVE or AUTOMATIC:
  1. 读取 actionMessagesactionErrorsfieldErrors 来自会话;
  2. 合并它们与当前操作的 actionMessagesactionErrorsfieldErrors
  3. remove 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. read actionMessages, actionErrors, fieldErrors from action;
      2. store 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天全站免登陆