简体   繁体   English

Spring bean验证消息解析

[英]Spring bean validation messages resolution

I want to do a very specific task, get all validation messages for every field in an Object . 我想做一个非常具体的任务,获取Object每个字段的所有验证消息。 First task is easy, getting all Annotations for field in Object , also recursively, was already done. 第一项任务很容易,也已经递归地获取了Object所有字段的Annotations (modified code from html5val dialect for thymeleaf) (来自thymeleaf的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();
}

Now I'm trying to get message for each annotation, with internationalization. 现在,我试图通过国际化为每个注释获取消息。

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

I'm getting internationalized messages, if something is resolvable by MessageSource , pretty good! 我正在收到国际化的消息,如果MessageSource可以解决某些问题,那就太好了! Sadly, but I cannot get messages for default messages, like org.hibernate.validator.constraints.Length.message , but it's not so problematic (I always can to provide these messages by my MessageSource ). 令人遗憾的是,但是我无法获取默认消息的消息,例如org.hibernate.validator.constraints.Length.message ,但这并不是问题(我总是可以通过MessageSource提供这些消息)。

To make this task fully working, I miss one thing, arguments for messages. 为了使此任务完全起作用,我错过了一件事,消息的参数。 So, resolved message looks like this. 因此,已解决的消息如下所示。 Alias length must be between {2} and {1} . Alias length must be between {2} and {1} BeanPropertyBindingResult have a method to reject values with arguments, but I don't know how to get it from Annotation. BeanPropertyBindingResult有一种方法可以拒绝带有参数的值,但是我不知道如何从注释中获取它。 It's probably done by Validator implementation, right? 这可能是由Validator实现完成的,对吧?

It's the same job as Spring DataBinder do for invalid fields, but I want to get this messages for custom validation messages in HTML5 Form Validation. 与Spring DataBinder处理无效字段的工作相同,但是我想在HTML5表单验证中获取此消息以用于自定义验证消息。 Is anyone known how to push bean through Spring internal use objects, to get these messages? 有谁知道如何通过Spring内部使用对象推送bean来获取这些消息?

And one important thing, everything is in thymeleaf context (it's my modification of html5val dialect) 还有一件重要的事,一切都在百里香的背景下(这是我对html5val方言的修改)

Solution is ugly. 解决方案很丑。 full solution is in my fork of HTML5 Validation Dialect https://bitbucket.org/globalbus/html5-validator-dialect-fork/commits/85da11b2c13dfbd90ff05a55014d792cb453d5e2 完整的解决方案在我的HTML5验证方言的分支中https://bitbucket.org/globalbus/html5-validator-dialect-fork/commits/85da11b2c13dfbd90ff05a55014d792cb453d5e2

For first, I need SpringValidatorAdapter.getArgumentsForConstraints() to be public. 首先,我需要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);
}
}

We need to provide a Validator object to constructor. 我们需要为构造Validator提供一个Validator对象。 We can obtain it from org.thymeleaf.Arguments passed to the Dialect 我们可以从传递给方言的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());

And now, really messy code 现在,代码非常混乱

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('')");
}

Clue is in SpringValidatorAdapter.getArgumentsForConstraints() , with valid arguments, can provide arguments for message resolution. 线索位于SpringValidatorAdapter.getArgumentsForConstraints() ,带有有效参数,可以提供用于消息解析的参数。 Internal Hibernate messages can be provided by MessageInterpolator.interpolate() Final resolution was provided by RequestContext.getMessage() . 内部Hibernate消息可由MessageInterpolator.interpolate()提供。最终解决方案由RequestContext.getMessage()

This not a "clean solution", I must test it in more use cases. 这不是“干净的解决方案”,我必须在更多用例中对其进行测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM