如果您熟悉Bean验证框架,那么您将无法获得方法参数的名称。因此,如果对方法的第一个参数执行@NotNull约束,并且验证失败,则getPropertyPath将类似于“ arg1”。
我想创建自己的@NotNull版本,该版本可以采用例如@NamedNotNull(“ emailAddress”)的值。但是我不知道如何在验证器中覆盖#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( 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 ) { ... }
Bean Validation 1.1引入了ParameterNameProvider用于在创建约束违反对象时为方法和构造函数参数提供名称的接口。
ParameterNameProvider
Hibernate Validator 5.2引入了ReflectionParameterNameProvider该类,该ParameterNameProvider实现使用反射来获取实际的参数名称(为了正常工作,它要求使用-parameters编译器参数来编译这些类):
ReflectionParameterNameProvider
-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实现一起使用:
@XxxParam
JerseyParameterNameProvider
/** * 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可以使用ValidationConfigclass 进行定制,以验证Jersey资源类/方法的使用,并通过以下ContextResolver<T>机制将其公开:
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。
ValidationConfigurationContextResolver
ResourceConfig
有关更多详细信息,请参阅Jersey文档中有关Bean验证的支持。