我可以更改Method参数的ConstraintValidator中的属性路径吗? [英] Can I change the property path in a ConstraintValidator for Method arguments?

查看:88
本文介绍了我可以更改Method参数的ConstraintValidator中的属性路径吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我想创建我自己的@NotNull版本, @NamedNotNull(emailAddress)。但我无法弄清楚如何覆盖我的Validator中的#getPropertyPath?有没有办法做到这一点,或者我坚持arg1或arg2等。



编辑



基于我收到的答案,我能够想出以下实现,它允许我从@QueryParam或@PathParam注释中获取值,并将它们用作Bean的属性路径验证注释,如@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(方法方法){
Annotation [] [] annotationsByParam = method.getParameterAnnotations();
列表< String> names = new ArrayList<>(annotationsByParam.length); (Annotation [] annotations:annotationsByParam)
{
String name = getParamName(annotations);
if(name == null)
name =arg+(names.size()+ 1);

names.add(name);
}

返回名称;

$ b $ 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();
}
}

返回null;


$ b $ / code>

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

  register(ValidationConfigurationContextResolver.class); 

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

  public void getUser(
@NotNull @QueryParam(emailAddress)String emailAddress,
@NotNull @QueryParam(password)字符串密码)
{...}


解决方案

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






Hibernate Validator 5.2引入了 ReflectionParameterNameProvider class,a ParameterNameProvider 的实现使用反射来获取实际的参数名称(要正常工作,它需要使用参数编译类 / **
*使用Java 8反射来获取参数名称。
*< p>
*< p>对于此提供程序返回实际参数名称,必须使用'-parameters'编译器
*参数编译类。否则,JDK将以{@code arg0},{@code arg1}等形式返回合成名称。< / p>
*< p>
*< p>请参阅< 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(构造函数<?>构造函数){
return getParameterNames(constructor.getParameters());
}

@Override
public List< String> getParameterNames(Method method){
return getParameterNames(method.getParameters());
}

私人列表< String> getParameterNames(Parameter [] parameters){

List< String> parameterNames = newArrayList();
for(Parameter parameter:parameters){
//如果在编译时使用'-parameters',将返回实际名称。否则,它将是arg0,arg1 ...
parameterNames.add(parameter.getName());
}

return parameterNames;


$ / code $ / pre

$ hr

Dropwizard对其进行了扩展,并使用 @XxxParam 注释的支持/apidocs/io/dropwizard/jersey/validation/JerseyParameterNameProvider.htmlrel =nofollow noreferrer> JerseyParameterNameProvider ,它也可以与其他JAX-RS实现一起工作:

/ **
*在hibernate验证器中为参数名称发现添加球衣支持。
*< p>
*< p>这个提供程序的行为与Hibernate提供的{@link ReflectionParameterNameProvider}行为相似,除非
*方法参数使用球衣参数注释进行注释,如{@link QueryParam}。如果球衣参数
*注释存在,则注释的值将用作参数名称。< / p>
* /
public class JerseyParameterNameProvider extends ReflectionParameterNameProvider {

@Override
public List< String> getParameterNames(Method method){
Parameter [] parameters = method.getParameters();
Annotation [] [] parameterAnnotations = method.getParameterAnnotations();
列表< 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);
}
返回名称;
}

/ **
*从其注释中派生成员的名称和类型
* /
public static可选< String> getParameterNameFromAnnotations(Annotation [] memberAnnotations){
$ b $ for(Annotation a:memberAnnotations){
if(QueryParam的一个实例){
return Optional.of(query param+( (QueryParam)a).value());
} else if(PathParam的一个实例){
return Optional.of(path param+((PathParam)a).value());
} else if(HeaderParam的一个实例){
return Optional.of(header+((HeaderParam)a).value());
} else if(CookieParam的一个实例){
return Optional.of(cookie+((CookieParam)a).value());
} else if(FormParam的一个实例){
return Optional.of(form field+((FormParam)a).value());
} else if(一个Context的实例){
return Optional.of(context);
} else if(一个MatrixParam的实例){
return Optional.of(matrix param+((MatrixParam)a).value());
}
}

return Optional.empty();




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






定制 Validator 用于验证Jersey资源类/方法可以使用 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;


$ / code>

然后注册 ValidationConfigurationContextResolver in ResourceConfig



请参阅有关Bean验证支持的Jersey文档以获取更多详细信息。


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 of a method and the validation fails the getPropertyPath will be something like "arg1".

I would like to create my own version of @NotNull that can take a value e.g. @NamedNotNull( "emailAddress" ). But I can't figure out how to override the #getPropertyPath in my Validator? Is there any way to do this or am I stuck with "arg1" or "arg2", etc.

EDIT

Based on the answer I received I was able to come up with the following implementation that allows me to take the value from my @QueryParam or @PathParam annotations and use those as the property path for Bean Validation annotations like @NotNull.

For Jersey you need to create the following class. Note the implementation of 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;
        }
    }
}

Then in your RestConfig you need to add the following line:

register( ValidationConfigurationContextResolver.class );

That's it. Now your ConstraintValidationExceptions will contain the name of the QueryParam or PathParam they are annotated with. For example:

 public void getUser( 
     @NotNull @QueryParam( "emailAddress" ) String emailAddress,
     @NotNull @QueryParam( "password" ) String password ) 
 { ... }

解决方案

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.
 * <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 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.
 * <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();
    }
}

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<T> mechanism:

public class ValidationConfigurationContextResolver 
        implements ContextResolver<ValidationConfig> {

    @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.

这篇关于我可以更改Method参数的ConstraintValidator中的属性路径吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆