我可以更改Method参数的ConstraintValidator中的属性路径吗? [英] Can I change the property path in a ConstraintValidator for Method arguments?
问题描述
如果您熟悉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引入了 Dropwizard对其进行了扩展,并使用 @XxxParam ReflectionParameterNameProvider
class,a ParameterNameProvider
的实现使用反射来获取实际的参数名称(要正常工作,它需要使用参数编译类
/ **
注释的支持/apidocs/io/dropwizard/jersey/validation/JerseyParameterNameProvider.htmlrel =nofollow noreferrer>
*使用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
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屋!