Can I change the property path in a ConstraintValidator for Method arguments?

后端 未结 2 575
挽巷
挽巷 2020-12-16 10:53

If you are familiar with the Bean Validation Framework you know that you cannot get the name of a method argument. So if you do a @NotNull constraint on the first argument o

2条回答
  •  南笙
    南笙 (楼主)
    2020-12-16 11:32

    Bean Validation 1.1 introduced the ParameterNameProvider interface for providing names for method and constructor parameters when creating constraint violation objects.


    Hibernate Validator 5.2 introduced the ReflectionParameterNameProvider class, a ParameterNameProvider implementation that uses reflection to get the actual parameter names (to work properly, it requires the classes to be compiled with the -parameters compiler argument):

    /**
     * Uses Java 8 reflection to get the parameter names.
     * 

    *

    For this provider to return the actual parameter names, classes must be compiled with the '-parameters' compiler * argument. Otherwise, the JDK will return synthetic names in the form {@code arg0}, {@code arg1}, etc.

    *

    *

    See also JEP 118

    * * @author Khalid Alqinyah * @since 5.2 */ public class ReflectionParameterNameProvider implements ParameterNameProvider { @Override public List getParameterNames(Constructor constructor) { return getParameterNames(constructor.getParameters()); } @Override public List getParameterNames(Method method) { return getParameterNames(method.getParameters()); } private List getParameterNames(Parameter[] parameters) { List parameterNames = newArrayList(); for (Parameter parameter : parameters) { // If '-parameters' is used at compile time, actual names will be returned. Otherwise, it will be arg0, arg1... parameterNames.add(parameter.getName()); } return parameterNames; } }

    Dropwizard extends it and add support to JAX-RS @XxxParam annotations with the JerseyParameterNameProvider that should work with other JAX-RS implementations too:

    /**
     * Adds jersey support to parameter name discovery in hibernate validator.
     * 

    *

    This provider will behave like the hibernate-provided {@link ReflectionParameterNameProvider} except when a * method parameter is annotated with a jersey parameter annotation, like {@link QueryParam}. If a jersey parameter * annotation is present the value of the annotation is used as the parameter name.

    */ public class JerseyParameterNameProvider extends ReflectionParameterNameProvider { @Override public List getParameterNames(Method method) { Parameter[] parameters = method.getParameters(); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); List names = new ArrayList<>(parameterAnnotations.length); for (int i = 0; i < parameterAnnotations.length; i++) { Annotation[] annotations = parameterAnnotations[i]; String name = getParameterNameFromAnnotations(annotations).orElse(parameters[i].getName()); names.add(name); } return names; } /** * Derives member's name and type from it's annotations */ public static Optional getParameterNameFromAnnotations(Annotation[] memberAnnotations) { for (Annotation a : memberAnnotations) { if (a instanceof QueryParam) { return Optional.of("query param " + ((QueryParam) a).value()); } else if (a instanceof PathParam) { return Optional.of("path param " + ((PathParam) a).value()); } else if (a instanceof HeaderParam) { return Optional.of("header " + ((HeaderParam) a).value()); } else if (a instanceof CookieParam) { return Optional.of("cookie " + ((CookieParam) a).value()); } else if (a instanceof FormParam) { return Optional.of("form field " + ((FormParam) a).value()); } else if (a instanceof Context) { return Optional.of("context"); } else if (a instanceof MatrixParam) { return Optional.of("matrix param " + ((MatrixParam) a).value()); } } return Optional.empty(); } }

    If you don't use Dropwizard, you can use the above code to create your own implementation.


    Customization of the Validator used in validation of Jersey resource classes/methods can be done using ValidationConfig class and exposing it via ContextResolver mechanism:

    public class ValidationConfigurationContextResolver 
            implements ContextResolver {
    
        @Override
        public ValidationConfig getContext(final Class type) {
            ValidationConfig config = new ValidationConfig();
            config.parameterNameProvider(new CustomParameterNameProvider());
            return config;
        }
    }
    

    Then register the ValidationConfigurationContextResolver in ResourceConfig.

    Refer to the Jersey documentation about Bean Validation support for more details.

提交回复
热议问题