2015-06-26 2 views
1

Я пересматриваю страницу в своем приложении и пытаюсь выполнить рекурсивную проверку на объекте, который сам имеет ограничения проверки. Однако, что происходит, я получаю ошибку, описывающую объект недействительным. Я бы хотел, чтобы ошибки проверки вложенного объекта возвращались на страницу.SpringValidator (net.sf.oval): Рекурсивная отчетность об ошибках

ConsumerManagementController.java

@Controller 
@SessionAttributes(ConsumerManagementController.CMD_NAME) 
public class ConsumerManagementController { 

    private org.springframework.validation.Validator validator; 

    public ConsumerManagementController() { 
     validator = new net.sf.oval.integration.spring.SpringValidator.SpringValidator(new net.sf.oval.Validator()); 
    } 

    @RequestMapping(value = FORM_VIEW, method = RequestMethod.POST) 
    protected ModelAndView processAdjustment(HttpServletRequest request, 
              Model model, 
              @ModelAttribute(CMD_NAME) ConsumerManagementCommand cmd, 
              BindingResult errors) throws Exception { 

     ... 

     // Validating the ConsumerManagementCommand object 
     validator.validate(cmd, errors); 
     // RESULT 
     // org.springframework.validation.BeanPropertyBindingResult: 1 errors 
     // Field error in object 'cmc' on field 'consumerAdjustment': rejected value [[com.company.core.dto.ConsumerAdjustment - memo=, -- ]]; codes [net.sf.oval.constraint.AssertValid.cmc.consumerAdjustment,net.sf.oval.constraint.AssertValid.consumerAdjustment,net.sf.oval.constraint.AssertValid.com.company.core.dto.ConsumerAdjustment,net.sf.oval.constraint.AssertValid]; arguments []; default message [com.company.web.ops.commands.consumer.account.ConsumerManagementCommand.consumerAdjustment is invalid] 


     // Validating the ConsumerAdjustment object 
     ConsumerAdjustment consumerAdjustment = cmd.getConsumerAdjustment(); 
     BeanPropertyBindingResult consumerAdjustmentErrors = new BeanPropertyBindingResult(consumerAdjustment, "consumerAdjustment"); 

     validator.validate(consumerAdjustment, consumerAdjustmentErrors); 

     // RESULT 
     // org.springframework.validation.BeanPropertyBindingResult: 2 errors 
     // Field error in object 'consumerAdjustment' on field 'memo': rejected value []; codes [consumerAdjustment.memo.err.null.consumerAdjustment.memo,consumerAdjustment.memo.err.null.memo,consumerAdjustment.memo.err.null.java.lang.String,consumerAdjustment.memo.err.null]; arguments []; default message [com.company.core.dto.ConsumerAdjustment.memo cannot be empty] 

     ... 

    } 
} 

ConsumerManagementCommand.java

@Guarded 
public class ConsumerManagementCommand implements Serializable{ 

    @AssertValid 
    private ConsumerAdjustment consumerAdjustment = new ConsumerAdjustment(); 

} 

ConsumerAdjustment.java

public class ConsumerAdjustment extends AbstractDTO implements Serializable { 

    @NotNull(errorCode = ERR_MEMO_NULL) 
    @NotEmpty(errorCode = ERR_MEMO_NULL) 
    @Length(max = 500, errorCode = ERR_MEMO_LENGTH) 
    private String memo; 

} 

См РЕЗУЛЬТАТ комментарии ConsumerManagementController для сообщения об ошибках.

ответ

0

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

Во-первых, я должен был создать свой собственный валидатор, расширив SpringValidator:

class ConsumerManagementCommandSpringValidator extends SpringValidator{ 

Logger log = org.slf4j.LoggerFactory.getLogger(ConsumerManagementCommandSpringValidator.class); 

public ConsumerManagementCommandSpringValidator(net.sf.oval.Validator validator) { 
    super(validator); 
} 

/** 
* {@inheritDoc} 
* Overridden method here to handle the nested validated objects of the {@link ConsumerManagementCommand} 
* type. If a nested object fails validation, we need to identify the path to the field using 
* the object name. 
*/ 
@Override 
public void validate(final Object objectToValidate, final Errors errors) 
{ 
    try 
    { 
     for (final ConstraintViolation violation : super.getValidator().validate(objectToValidate)) 
     { 
      final OValContext ctx = violation.getContext(); 
      final String errorCode = violation.getErrorCode(); 
      final String errorMessage = violation.getMessage(); 

      if (ctx instanceof FieldContext) { 
       Field field = ((FieldContext) ctx).getField(); 

       String fieldName = field.getName(); 
       try{ 
        errors.rejectValue(fieldName, errorCode, errorMessage); 
       }catch(NotReadablePropertyException nrpe){ 
        // Resolve the property location based on the object. Uses the class 
        // name of the field, tweaks the case and concatenates the value 
        StringBuilder objectReference = new StringBuilder(field.getDeclaringClass().getSimpleName()); 
        fieldName = objectReference.substring(0, 1).toLowerCase().concat(objectReference.substring(1)).concat(".").concat(fieldName); 

        errors.rejectValue(fieldName, errorCode, errorMessage); 
       } 
      } else{ 
       errors.reject(errorCode, errorMessage); 
      } 
     } 
    } catch (final ValidationFailedException ex) { 
     log.error("Unexpected error during validation", ex); 
     errors.reject(ex.getMessage()); 
    } 
} 

}

Во-вторых, я создал еще один валидатор, который расширяет net.sf.oval.Validator:

class ConsumerManagementCommandValidator extends net.sf.oval.Validator{ 

@Override 
protected void checkConstraintAssertValid(
     List<ConstraintViolation> violations, AssertValidCheck check, 
     Object validatedObject, Object valueToValidate, 
     OValContext context, String[] profiles) throws OValException { 

    if (valueToValidate == null) return; 

    // ignore circular dependencies 
    if (isCurrentlyValidated(valueToValidate)) return; 

    // changed here access to private class variable to getter 
    final List<ConstraintViolation> additionalViolations = getCollectionFactory().createList(); 
    validateInvariants(valueToValidate, additionalViolations, profiles); 

    if (additionalViolations.size() != 0) { 
     final String errorMessage = renderMessage(context, valueToValidate, check.getMessage(), check.getMessageVariables()); 

     violations.add(new ConstraintViolation(check, errorMessage, validatedObject, valueToValidate, context, additionalViolations)); 
     //add the violations to parent list :-) 
     violations.addAll(additionalViolations); 
    } 
} 

@Override 
public List<ConstraintViolation> validate(Object validatedObject, String... profiles) throws IllegalArgumentException, ValidationFailedException { 
    // TODO Auto-generated method stub 
    return super.validate(validatedObject, profiles); 
} 

}

Теперь я могу создать мой валидатор для использования:

private ConsumerManagementCommandSpringValidator validator = new ConsumerManagementCommandSpringValidator(new ConsumerManagementCommandValidator()); 

Где я называю Validate:

// Validate the form 
validator.validate(cmd, errors); 

Теперь для объекта Command. Поскольку у меня есть два объекта в объекте команды, мне нужно было проверить их в зависимости от условия. AssertValid с яваскриптом EL:

// Custom assertion here, when the selected option is defined, only validate which is necessary 
// We're using javascript expression language to do the comparison 
@AssertValid(when="js:_this.selectedOption == com.company.core.adjustment.ConsumerManagementOption.ADJUST_CARD_VALUE") 
private ConsumerAdjustment consumerAdjustment = new ConsumerAdjustment(); 
@AssertValid(when="js:_this.selectedOption == com.company.core.adjustment.ConsumerManagementOption.COMBINE_CARDS") 
private ConsumerCombineAccounts consumerCombineAccounts = new ConsumerCombineAccounts(); 

Теперь, когда я проверить объект команды, вложенные объекты, которые требуют проверок будут обрабатывать и ошибка будет пузыриться до уровня команды объекта.