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

Validation Struts2在单个文本字段上验证3次,validation,struts2,interceptor,interceptorstack,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",

我对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>
当我在txtRequestDateFrom字段中输入字母时,我在上收到3条验证消息

<s:fielderror fieldName="txtRequestDateFrom"/> 
我有自己的自定义主题,我相信简单主题不会有太多修改。我的拦截器堆栈与默认值堆栈几乎相同

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

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

请在这方面帮助我,你需要理解

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

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

错误报告有两种方式:

  • 全局使用转换错误拦截器
  • 在每个字段的基础上,使用转换验证器

  • 您正在使用这两种机制,从而重复发现的错误。正如文档所述,通常您不想报告所有转换错误,因此应该从堆栈中删除。现在,您可以使用
    转换
    验证程序有选择地将转换错误作为字段错误引发。

    我发现我的自定义
    日期时间转换器
    导致了异常和额外的错误消息。因为我从Struts2书中找到了下面的代码,以便更改日期的正常格式。当它抛出异常时,它在控制台上显示异常,在字段error上显示错误消息,而不是将异常传递给验证器。我认为这是一种bug,因为这个类扩展了
    strutstypecoverter
    ,它应该像普通的转换器一样工作

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

    无论如何,我改变了
    抛出新的TypeConversionException(e)
    to
    返回null并在验证XML上添加了
    必需的
    验证器。现在,当我在日期字段中输入无效日期时,它显示了一个错误


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

    我昨天遇到了一个类似的问题,最终找到了一个我愿意分享的解决方案。我在操作中使用注释进行验证,因此我更改了默认struts拦截器堆栈,并将SensibleVersionErrorInterceptor而不是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();
      }
    

    感谢您的回答,但正如我之前在删除ConversionError拦截器时所说的,它只删除1条错误消息。剩下的两条消息中有一条来自Action-validation.xml。即使删除Action-validation.xml,也始终会保留一条错误消息。我不知道它是从哪里来的。此外,当转换错误发生时,它总是在控制台上给出异常。若它正在处理错误,为什么它会在控制台上显示异常?我认为是其他原因导致了这个错误异常,它给我的是:java.lang.NoSuchMethodException:com.*********.client.CEST01.setTxtRequestDateFrom([Ljava.lang.String;)@batbaatar看起来您已打开,这将引发通常忽略的转换错误。关于附加消息的问题,我怀疑您的堆栈中仍然存在
    conversionError
    拦截器。您能否检查
    defaultStack
    定义中引用的任何堆栈是否具有offen拦截程序?另一种确保
    conversionError
    拦截程序不在堆栈中的方法是,尝试从
    struts.xml
    中注释拦截程序定义
    。我的devMode为false。我发现了我的问题,我想Struts2在错误处理方面做得不太好。
    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;
        }
    }
    
    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...
       }
    }