代码之家  ›  专栏  ›  技术社区  ›  robert_difalco

我可以更改方法参数的ConstraintValidator中的属性路径吗?

  •  23
  • robert_difalco  · 技术社区  · 10 年前

    如果您熟悉Bean验证框架,那么您知道无法获取方法参数的名称。因此,如果对方法的第一个参数执行@NotNull约束,并且验证失败,则getPropertyPath将类似于“arg1”。

    我想创建我自己的@NotNull版本,它可以接受一个值,例如@NamedNotNull(“emailAddress”)。但我无法确定如何在Validator中重写#getPropertyPath?是否有任何方法可以做到这一点,或者我被“arg1”或“arg2”等卡住了。

    编辑

    根据我收到的答案,我能够想出以下实现,它允许我从@QueryParam或@PathParam注释中获取值,并将这些值用作Bean Validation注释(如@NotNull)的属性路径。

    对于Jersey,您需要创建以下类。注意DefaultParameterNameProvider的实现:

    public class ValidationConfigurationContextResolver implements ContextResolver<ValidationConfig> {
        @Override
        public ValidationConfig getContext( final Class<?> type ) {
            final ValidationConfig config = new ValidationConfig();
            config.parameterNameProvider( new RestAnnotationParameterNameProvider() );
            return config;
        }
    
        static class RestAnnotationParameterNameProvider extends DefaultParameterNameProvider {
    
            @Override
            public List<String> getParameterNames( Method method ) {
                Annotation[][] annotationsByParam = method.getParameterAnnotations();
                List<String> names = new ArrayList<>( annotationsByParam.length );
                for ( Annotation[] annotations : annotationsByParam ) {
                    String name = getParamName( annotations );
                    if ( name == null )
                        name = "arg" + ( names.size() + 1 );
    
                    names.add( name );
                }
    
                return names;
            }
    
            private static String getParamName( Annotation[] annotations ) {
                for ( Annotation annotation : annotations ) {
                    if ( annotation.annotationType() == QueryParam.class ) {
                        return QueryParam.class.cast( annotation ).value();
                    }
                    else if ( annotation.annotationType() == PathParam.class ) {
                        return PathParam.class.cast( annotation ).value();
                    }
                }
    
                return null;
            }
        }
    }
    

    然后在RestConfig中,需要添加以下行:

    register( ValidationConfigurationContextResolver.class );
    

    就是这样。现在,ConstraintValidationExceptions将包含它们被注释的QueryParam或PathParam的名称。例如:

     public void getUser( 
         @NotNull @QueryParam( "emailAddress" ) String emailAddress,
         @NotNull @QueryParam( "password" ) String password ) 
     { ... }
    
    2 回复  |  直到 8 年前
        1
  •  11
  •   Hardy    10 年前

    如果您熟悉Bean验证框架,那么您知道 无法获取方法参数的名称

    这不太正确。Bean Validation指定了 ParameterNameProvider 这允许您提供自己的实现。Hibernate Validator与 ParaNamer 以提供参数名称。请参见验证程序 online docs 了解更多信息。一旦Validator支持Java8,它也将支持Java8参数命名功能。

    IMO,你应该试试ParaNamer。

        2
  •  8
  •   cassiomolin    7 年前

    Bean Validation 1.1介绍了 ParameterNameProvider 接口,用于在创建约束冲突对象时为方法和构造函数参数提供名称。


    Hibernate Validator 5.2引入了 ReflectionParameterNameProvider 类,a 参数名称提供程序 使用反射获取实际参数名称的实现(要正常工作,需要使用 -parameters 编译器参数):

    /**
     * Uses Java 8 reflection to get the parameter names.
     * <p>
     * <p>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.</p>
     * <p>
     * <p>See also <a href="http://openjdk.java.net/jeps/118">JEP 118</a></p>
     *
     * @author Khalid Alqinyah
     * @since 5.2
     */
    public class ReflectionParameterNameProvider implements ParameterNameProvider {
    
        @Override
        public List<String> getParameterNames(Constructor<?> constructor) {
            return getParameterNames(constructor.getParameters());
        }
    
        @Override
        public List<String> getParameterNames(Method method) {
            return getParameterNames(method.getParameters());
        }
    
        private List<String> getParameterNames(Parameter[] parameters) {
    
            List<String> 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对其进行了扩展,并为JAX-RS添加了支持 @XxxParam 注释 JerseyParameterNameProvider 这也应该与其他JAX-RS实现一起工作:

    /**
     * Adds jersey support to parameter name discovery in hibernate validator.
     * <p>
     * <p>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.</p>
     */
    public class JerseyParameterNameProvider extends ReflectionParameterNameProvider {
    
        @Override
        public List<String> getParameterNames(Method method) {
            Parameter[] parameters = method.getParameters();
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            List<String> 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<String> 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();
        }
    }
    

    如果不使用Dropwizard,可以使用上面的代码创建自己的实现。


    自定义 Validator 可以使用 ValidationConfig 类,并通过 ContextResolver<T> 机制:

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

    然后注册 ValidationConfigurationContextResolver 在里面 ResourceConfig .

    请参阅 Jersey documentation about Bean Validation support 了解更多详情。