【问题标题】:Spring bean validation messages resolutionSpring bean 验证消息解析
【发布时间】:2015-04-17 23:19:59
【问题描述】:

我想做一个非常具体的任务,获取Object 中每个字段的所有验证消息。 第一个任务很简单,在Object 中获取所有Annotations 字段,同样递归,已经完成。 (百里香的html5val方言修改代码)

private List<Annotation> fieldAnnotations() {
    Field field = this.fieldFinder.findField(this.targetClass, this.targetFieldName);
    if (field != null) {
        List<Annotation> annotations = Arrays.asList(field.getAnnotations());
        List<Annotation> toAdd = new ArrayList<>();
        for(Annotation a:annotations)
            if(a.annotationType().isAssignableFrom(Valid.class)){
                toAdd.addAll(new AnnotationExtractor(field.getType()).getAnnotationsForField(this.targetFieldName));
            }
            else
                toAdd.add(a);
        return toAdd;
    }
    return Collections.emptyList();
}

现在我正在尝试通过国际化获取每个注释的消息。

    BeanPropertyBindingResult binding = new BeanPropertyBindingResult(realObject, root);
    for (Annotation constraint : constraints) {
        String message = AnnotationExtractor.getDefaultMessage(constraint);
        binding.rejectValue(fieldName, constraint.annotationType().getSimpleName(), message); 
    }
    List<ObjectError> errors = binding.getAllErrors();
    RequestContext requestContext = (RequestContext) arguments.getContext().getVariables().get(SpringContextVariableNames.SPRING_REQUEST_CONTEXT);
    for(ObjectError e:errors){
        String s =requestContext.getMessage(e, true);
    }

我收到了国际化消息,如果 MessageSource 可以解决问题,那就太好了! 可悲的是,我无法获取默认消息的消息,例如org.hibernate.validator.constraints.Length.message,但这不是问题(我总是可以通过我的MessageSource 提供这些消息)。

为了使这项任务充分发挥作用,我想念一件事,即消息的参数。因此,已解决的消息如下所示。 Alias length must be between {2} and {1}BeanPropertyBindingResult 有一种方法可以拒绝带参数的值,但我不知道如何从 Annotation 中获取它。应该是Validator实现的吧?

这与 Spring DataBinder 对无效字段所做的工作相同,但我想在 HTML5 表单验证中为自定义验证消息获取此消息。有谁知道如何通过 Spring 内部使用对象推送 bean 以获取这些消息?

还有一件重要的事情,一切都在 thymeleaf 上下文中(这是我对 html5val 方言的修改)

【问题讨论】:

    标签: java spring bean-validation thymeleaf


    【解决方案1】:

    解决方案很难看。 完整的解决方案在我的 HTML5 Validation Dialect 的 fork 中 https://bitbucket.org/globalbus/html5-validator-dialect-fork/commits/85da11b2c13dfbd90ff05a55014d792cb453d5e2

    首先,我需要 SpringValidatorAdapter.getArgumentsForConstraints() 公开。

    public class MyValidationAdapter extends SpringValidatorAdapter{
    public MyValidationAdapter(Validator targetValidator) {
        super(targetValidator);
    }
    @Override
    public Object[] getArgumentsForConstraint(String objectName,
            String field, ConstraintDescriptor<?> descriptor) {
        return super.getArgumentsForConstraint(objectName, field, descriptor);
    }
    @Override
    public void processConstraintViolations(
            Set<ConstraintViolation<Object>> violations, Errors errors) {
        super.processConstraintViolations(violations, errors);
    }
    }
    

    我们需要为构造函数提供一个Validator 对象。我们可以从传递给方言的org.thymeleaf.Arguments获取它

    this.requestContext = (RequestContext) this.arguments.getContext().getVariables().get(SpringContextVariableNames.SPRING_REQUEST_CONTEXT);
    
    this.validatorFactory = this.requestContext.getWebApplicationContext().getBean(ValidatorFactory.class);
    
    this.validator = new MyValidationAdapter(this.validatorFactory.getValidator());
    

    现在,代码真的很乱

    public void processField(Element fieldElement, String fieldName){
        BeanPropertyBindingResult binding = new BeanPropertyBindingResult(
                this.realObject, this.root);
        PropertyDescriptor rootProp = this.beanDescriptor
                .getConstraintsForProperty(fieldName);
        List<PropertyDescriptor> finalProp = new LinkedList<PropertyDescriptor>();
        if (rootProp.isCascaded())// if it's nested, scan all properties for
                                // annotation
            finalProp.addAll(this.validator.getConstraintsForClass(
                    rootProp.getElementClass()).getConstrainedProperties());
        else
            finalProp.add(rootProp);
        for (PropertyDescriptor prop : finalProp)
            for (final ConstraintDescriptor<?> desc : prop
                    .getConstraintDescriptors()) {
                Annotation constraint = desc.getAnnotation();
                String className = this.beanDescriptor.getElementClass()
                        .getSimpleName();
                className = Character.toLowerCase(className.charAt(0))
                        + className.substring(1);
                String field = className + "." + prop.getPropertyName();
                String errorCode = constraint.annotationType().getSimpleName();
                Object[] errorArgs = this.validator.getArgumentsForConstraint(
                        this.root, field, desc);
                String message = (String) desc.getAttributes().get(ANNOTATION_MESSAGE);
                if (INTERNAL.matcher(message).find())
                    message = this.validatorFactory.getMessageInterpolator().interpolate(
                            message, new Context() {
    
                                public Object getValidatedValue() {
                                    return null;
                                }
    
                                public ConstraintDescriptor<?> getConstraintDescriptor() {
                                    return desc;
                                }
                            });
                String[] errorCodes = binding.resolveMessageCodes(errorCode,
                        field);
                binding.addError(new FieldError(binding.getObjectName(), prop
                        .getPropertyName(), null, false, errorCodes, errorArgs,
                        message));
            }
    
        List<ObjectError> errors = binding.getAllErrors();
        StringBuilder customValidation = new StringBuilder();
        for (ObjectError e : errors) {
            String s = this.requestContext.getMessage(e, true);
            customValidation.append(s);
            if (!s.endsWith("."))
                customValidation.append(". ");
    
        }
        String onInvalid = String.format("this.setCustomValidity('%s')",
                customValidation.toString());
        fieldElement.setAttribute(ONINVALID_ATTR, onInvalid);
        fieldElement.setAttribute(ONCHANGE_ATTR, "this.setCustomValidity('')");
    }
    

    线索在SpringValidatorAdapter.getArgumentsForConstraints(),带有有效参数,可以为消息解析提供参数。 内部休眠消息可以由MessageInterpolator.interpolate() 提供 最终解决方案由RequestContext.getMessage()提供。

    这不是一个“干净的解决方案”,我必须在更多用例中对其进行测试。

    【讨论】:

      猜你喜欢
      • 2016-05-07
      • 2020-05-26
      • 2013-04-14
      • 2020-02-04
      • 2011-02-18
      • 2011-05-11
      • 1970-01-01
      • 2018-01-23
      • 1970-01-01
      相关资源
      最近更新 更多