Проверка Struts2 с использованием перехватчика хранилища сообщений

Учти это:

  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>

1 ответ

Решение
  1. пользователь обновляет страницу

  2. Ошибки ушли

Как сохранить ошибки при обновлении страницы?

Именно так работает MessageStoreInterceptor. На самом деле это особенность, а не ошибка.

Обновление страницы - это действие, запускаемое пользователем, это означает, что можно предположить, что он уже прочитал результат предыдущей операции (попытки входа в систему), если только он не нажимает клавишу F5 с закрытыми глазами.

Вы должны ХОЧУ сообщение истечь после первого чтения.

Рассмотрим страницу с множеством операций, не являющихся AJAX, такими как выпадающий список, зависящий от других и т. Д. Если сообщение об ошибке является постоянным, оно будет всплывать после каждой операции отправки, не связанной с переходом на другую страницу.

Вы этого не хотите. Вы должны захотеть получить сообщение о том, что одна операция пошла не так (или правильно) сразу после этой операции. Если пользователь затем продолжит выполнение других операций, таких как обновление, если эти операции не выполняются по ошибке (или в конкретном состоянии успеха), сообщение не должно отображаться.

После этого также возникает проблема удаления из сеанса постоянного сообщения, поскольку в противном случае вы получите это сообщение в следующем действии с RETRIEVE или же AUTOMATIC режим работы. Затем вы можете (но не должны) настраивать перехватчик MessageStore или самостоятельно записывать / читать / удалять сообщения из сеанса и обратно, в основном заново изобретая колесо. Или даже поставить два перехватчика MessageStore, один RETRIEVE и один STORE за displayLoginPage Действие, получение только что упомянутых подводных камней.

Вы также используете шаблон PRG (Post/Redirect/Get), чтобы избежать повторной отправки данных при обновлении страницы. В то же время вам следует избегать повторного чтения одних и тех же сообщений дважды.

Чтобы увидеть, как это конкретно работает, вы можете взглянуть на исходный код перехватчика MessageStore, который довольно прост:

  • До вызова, если действие ValidationAware а также operationMode является RETRIEVE или же AUTOMATIC:
    1. читать actionMessages, actionErrors, fieldErrors с сессии;
    2. объединить их с текущими действиями actionMessages, actionErrors, fieldErrors;
    3. Удалить actionMessages, actionErrors, fieldErrors с сессии.
/**
 * 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);
        }
    }
}
  • После вызова, если действие ValidationAware а также operationMode является STORE или же (operationMode является AUTOMATIC и результат имеет тип redirectAction):
    1. читать actionMessages, actionErrors, fieldErrors от действия;
    2. хранить actionMessages, actionErrors, fieldErrors в сессии.
/**
 * 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 секунд, не заботясь о том, прочитал их пользователь или нет... и это против удобства использования, ИМХО.

Другие вопросы по тегам