Struts2 проверяет 3 раза одно текстовое поле

У меня очень неприятная проблема со Struts (2.2.3). Вот мои проверки полей в ActionName-validation.xml

<field name="txtRequestDateFrom">
   <field-validator type="conversion">
      <param name="repopulateField">false</param>
      <message>${getText("E011", {"Date from"})}</message>
   </field-validator>
</field>

У меня нет метода validate() в моем классе действий. И у меня есть это в моем классе действий:

private Date txtRequestDateFrom;
{getter, setters}

Когда я ввожу буквы в поле txtRequestDateFrom, я получаю 3 сообщения проверки на

<s:fielderror fieldName="txtRequestDateFrom"/> 

Это выглядит так

Invalid field value for field "txtRequestDateFrom".
Invalid field value for field "txtRequestDateFrom".
Date from has an invalid value

У меня есть своя собственная тема, и я уверен, что от темы SIMPLE не так уж много модификаций. Мой стек перехватчиков почти такой же, как стек значений по умолчанию.

<interceptor-stack name="defaultStack">
        <interceptor-ref name="security"/>
            <interceptor-ref name="exception"/>
            <interceptor-ref name="alias"/>
            <interceptor-ref name="servletConfig"/>
            <interceptor-ref name="i18n"/>
            <interceptor-ref name="prepare"/>
            <interceptor-ref name="chain"/>
            <interceptor-ref name="debugging"/>
            <interceptor-ref name="scopedModelDriven"/>
            <interceptor-ref name="modelDriven"/>
            <interceptor-ref name="fileUploadStack" />
            <interceptor-ref name="fileUpload" >
            <param name="maximumSize">4000000</param>
        </interceptor-ref> 
            <interceptor-ref name="checkbox"/>
            <interceptor-ref name="multiselect"/>
            <interceptor-ref name="staticParams"/>
            <interceptor-ref name="actionMappingParams"/>                   
        <interceptor-ref name="params"/>
        <interceptor-ref name="conversionError" />
        <interceptor-ref name="validation">
            <param name="excludeMethods">execute, complete ...</param>
        </interceptor-ref>
        <interceptor-ref name="workflow"/>

    </interceptor-stack>

Я обнаружил, что одну ошибку поля можно убрать, удалив из стека перехватчик convertError. Но я не думаю, что это вызовет эту проблему. Struts должен уметь показывать ошибки, определенные разработчиком, не так ли?

Пожалуйста помоги мне с этим


person batbaatar    schedule 26.01.2012    source источник


Ответы (3)


Вам необходимо понять, как Struts2 обрабатывает ошибки преобразования.

О любой ошибке, возникающей во время преобразования типа, может или не нужно сообщать. Например, сообщение о том, что ввод abc не может быть преобразован в число, может быть важным. С другой стороны, сообщение о том, что пустая строка , не может быть преобразована в число, может не иметь значения, особенно в веб-среде, где трудно отличить пользователя, не вводящего значение, от ввода пустого значения.

...

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

Сообщение об ошибке может появиться двумя способами:

  1. Глобально, используя перехватчик ошибок преобразования
  2. Для каждого поля с использованием валидатора конверсии

Вы используете оба механизма, тем самым дублируя найденные ошибки. Как указано в документации, обычно вы не хотите сообщать обо всех ошибках преобразования и поэтому должны удалить ConversionErrorInterceptor из стека. Теперь вы можете выборочно поднимать ошибки конверсии как ошибки поля, используя валидатор conversion.

person Asa    schedule 26.01.2012
comment
Спасибо за ответ, но, как я уже говорил, когда я удаляю перехватчик ConversionError, он удаляет только одно сообщение об ошибке. Одно из двух оставшихся сообщений взято из Action-validation.xml. Даже когда я удаляю Action-validation.xml, всегда остается одно сообщение об ошибке. Я не знаю, откуда это исходит. Кроме того, когда происходит ошибка конвертации, она всегда выдает исключения на консоли. Если он обрабатывает ошибку, почему он показывает исключения на консоли? Я думаю, что есть что-то еще, вызывающее эту ошибку - person batbaatar; 26.01.2012
comment
Исключение, которое он мне дает: java.lang.NoSuchMethodException: com.*******.client.CEST01.setTxtRequestDateFrom([Ljava.lang.String;) - person batbaatar; 26.01.2012
comment
@batbaatar Похоже, у вас включен devMode, который вызовет обычно игнорируемые ошибки преобразования. По поводу проблемы с доп.сообщением, подозреваю, что у вас в стеке до сих пор стоит перехватчик conversionError. Можете ли вы проверить, есть ли в каком-либо из упомянутых стеков внутри определения defaultStack перехватчик-нарушитель? - person Asa; 27.01.2012
comment
Еще один способ убедиться, что перехватчика conversionError нет в вашем стеке, — попробовать закомментировать определение перехватчика <interceptor name="conversionError" class="no.more.cng.webapp.interceptor.SelectiveTypeConversionErrorInterceptor"/> из struts.xml. - person Asa; 27.01.2012
comment
Мой devMode был ложным. Я нашел свою проблему и думаю, что Struts2 не очень хорошо справляется с обработкой ошибок. - person batbaatar; 27.01.2012

Я обнаружил, что мой пользовательский DateTimeConverter вызывает исключения и дополнительное сообщение об ошибке. Потому что я нашел приведенный ниже код из книги Struts2, чтобы изменить нормальный формат моей даты. Когда он выдает исключение, он показывает исключение на консоли и сообщение об ошибке при ошибке поля, а не передает исключение валидатору. Я думаю, что это своего рода ошибка, потому что этот класс расширяет StrutsTypeConverter и должен работать как обычные преобразователи.

public class StringToDateTimeConverter extends StrutsTypeConverter {

private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd");

public Object convertFromString(Map context, String[] strings, Class toClass) {     
    if (strings == null || strings.length == 0 || strings[0].trim().length() == 0) {
        return null;
    }

    try {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(DATETIME_FORMAT.parse(strings[0]));
        calendar.set(Calendar.HOUR, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        return calendar.getTime();

    } catch (ParseException e) {
        throw new TypeConversionException(e);
    }
}

public String convertToString(Map context, Object date) {
    if (date != null && date instanceof Date) {
        return DATETIME_FORMAT.format(date);
    } else {
        return null;
    }
}

}

В любом случае я изменил throw new TypeConversionException(e); на return null; и добавил валидатор REQUIRED в валидационный XML. Теперь он показывает мне ошибку, когда я помещаю неверную дату в свои поля даты.

PS: есть ли другой способ изменить глобальный формат даты Struts? Спасибо

person batbaatar    schedule 27.01.2012

Вчера я столкнулся с аналогичной проблемой и, наконец, нашел решение, которым хочу поделиться. Я использую аннотации в своих действиях для проверки, поэтому я изменил стек перехватчиков Strut по умолчанию и поместил свой SensibleConversionErrorInterceptor вместо StrutsConversionErrorInterceptor. Это полностью идентично, но не создает никаких ошибок проверки. Вместо этого они генерируются проверкой, настроенной в аннотациях в моих действиях.

Вот мой преобразователь:

public class SensibleConversionErrorInterceptor extends StrutsConversionErrorInterceptor {

   private static final long serialVersionUID = 8186282792289268544L;

   @Override
   public String intercept(ActionInvocation invocation) throws Exception {

      ActionContext invocationContext = invocation.getInvocationContext();
      Map<String, Object> conversionErrors = invocationContext.getConversionErrors();
      ValueStack stack = invocationContext.getValueStack();

      HashMap<Object, Object> fakie = null;

      for (Map.Entry<String, Object> entry : conversionErrors.entrySet()) {
         String propertyName = entry.getKey();
         Object value = entry.getValue();

         if (shouldAddError(propertyName, value)) {
            // removed cause error messages are generated from annotations in actions
            // String message = XWorkConverter.getConversionErrorMessage(propertyName, stack);
            // Object action = invocation.getAction();
            // if (action instanceof ValidationAware) {
            //    ValidationAware va = (ValidationAware) action;
            //    va.addFieldError(propertyName, message);
            // }

            if (fakie == null) {
               fakie = new HashMap<Object, Object>();
            }

            fakie.put(propertyName, getOverrideExpr(invocation, value));
         }
      }

      if (fakie != null) {
         // if there were some errors, put the original (fake) values in place right before the result
         stack.getContext().put(ORIGINAL_PROPERTY_OVERRIDE, fakie);
         invocation.addPreResultListener(new PreResultListener() {
            public void beforeResult(ActionInvocation invocation, String resultCode) {
               Map<Object, Object> fakie = (Map<Object, Object>) invocation.getInvocationContext().get(ORIGINAL_PROPERTY_OVERRIDE);
               if (fakie != null) {
                  invocation.getStack().setExprOverrides(fakie);
               }
            }
        });
     }
     return invocation.invoke();
  }

}

И пример действия:

@Conversion
public class ProductAction extends ActionSupport {

   private Product product;
   // getter, setter and so on...

   @Action(...)
   @Validations(
       requiredFields = {
           @RequiredFieldValidator(
               type = ValidatorType.FIELD,
               fieldName = "product.validFrom",
               message = "required.product.validFrom",
               shortCircuit = true
           )
       },
       conversionErrorFields = {
          @ConversionErrorFieldValidator(
              fieldName = "product.validFrom",
              key = "invalid.fieldvalue.product.validFrom'",
              shortCircuit = true
          ) 
       }
   )
   public String saveOrUpdate() {
      // do something here...
   }
} 
person GreenTurtle    schedule 04.05.2012