Struts2在单个文本字段上验证3次

时间:2012-01-26 08:46:57

标签: validation struts2 interceptor interceptorstack

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

我发现通过从堆栈中删除conversionError拦截器可以删除一个字段错误。但我认为这不会导致这个问题。 Struts应该能够显示仅由开发人员定义的错误,对吗?

请帮我解决这个问题

3 个答案:

答案 0 :(得分:1)

您需要了解how Struts2 handles conversion errors

  

在类型转换期间发生的任何错误可能希望报告,也可能不希望报告。例如,报告输入“abc”无法转换为数字可能很重要。另一方面,报告空字符串“”无法转换为数字可能并不重要 - 尤其是在难以区分未输入值的用户与输入空白值的Web环境中。

...

  

重要的是要知道这些错误都不会直接报告。相反,它们被添加到ActionContext中名为conversionErrors的映射中。有几种方法可以访问此映射,并相应地报告错误。

     

可以通过两种方式进行错误报告:

     
      
  1. 在全球范围内,使用转换错误拦截器
  2.   
  3. 基于每个字段,使用转换验证程序
  4.   

您正在使用这两种机制,因此会重复发现的错误。正如文档所述,通常您不希望报告所有转换错误,因此应从堆栈中删除ConversionErrorInterceptor。现在,您可以使用conversion验证程序选择性地将转换错误提升为字段错误。

答案 1 :(得分:1)

我发现我的自定义DateTimeConverter导致了异常和额外的错误消息。因为我从Struts2书中找到了以下代码,以便更改我的Date的正常格式。当它抛出异常时,它会在控制台上显示异常并在字段错误上显示错误消息,而不是将异常传递给验证程序。我认为它有点像bug,因为这个类扩展了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;并在验证XML上添加了REQUIRED验证程序。现在,当我在日期字段上放置无效日期时,它会显示错误。

PS:还有其他方法可以更改Struts全局日期格式吗?感谢

答案 2 :(得分:0)

我昨天遇到了类似的问题,终于找到了一个我喜欢分享的解决方案。我在我的动作中使用注释进行验证,所以我更改了默认的struts拦截器堆栈并将我的SensibleConversionErrorInterceptor放入StrutsConversion错误中。这一个完全相同,但不会产生任何验证错误。相反,它们是通过我的操作中的注释中配置的验证生成的。

这是我的转换器:

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