java - bean - ¿Puedo cambiar la ruta de la propiedad en un ConstraintValidator para los argumentos del método?
bean validation (2)
Si está familiarizado con el Marco de Validación de Bean, sabe que no puede obtener el nombre de un argumento de método
Eso no es del todo correcto. La validación de Bean especifica el concepto de un ParameterNameProvider que le permite proporcionar su propia implementación. Hibernate Validator se integra con ParaNamer para proporcionar nombres de parámetros. Consulte los documentos en línea del validador para obtener más información. Una vez que Validator admita Java 8, también admitirá la función de nombres de parámetros de Java 8.
OMI, deberías darle una oportunidad a ParaNamer.
Si está familiarizado con Bean Validation Framework, sabe que no puede obtener el nombre de un argumento de método. Entonces, si realiza una restricción @NotNull en el primer argumento de un método y la validación falla, getPropertyPath será algo así como "arg1".
Me gustaría crear mi propia versión de @NotNull que puede tomar un valor, por ejemplo, @NamedNotNull ("emailAddress"). ¿Pero no puedo averiguar cómo anular el #getPropertyPath en mi Validator? ¿Hay alguna manera de hacer esto o estoy atrapado con "arg1" o "arg2", etc.
EDITAR
Según la respuesta que recibí, pude encontrar la siguiente implementación que me permite tomar el valor de mis anotaciones @QueryParam o @PathParam y usarlas como la ruta de la propiedad para las anotaciones de Validación de Bean como @NotNull.
Para Jersey necesitas crear la siguiente clase. Tenga en cuenta la implementación de 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;
}
}
}
Luego, en tu RestConfig necesitas agregar la siguiente línea:
register( ValidationConfigurationContextResolver.class );
Eso es. Ahora sus ConstraintValidationExceptions contendrán el nombre del QueryParam o PathParam con el que están anotados. Por ejemplo:
public void getUser(
@NotNull @QueryParam( "emailAddress" ) String emailAddress,
@NotNull @QueryParam( "password" ) String password )
{ ... }
Bean Validation 1.1 introdujo la interfaz ParameterNameProvider
para proporcionar nombres para los parámetros del método y del constructor al crear objetos de infracción de restricción.
Hibernate Validator 5.2 introdujo la clase ReflectionParameterNameProvider
, una implementación de ParameterNameProvider
que usa la reflexión para obtener los nombres de los parámetros reales (para funcionar correctamente, requiere que las clases se compilen con el argumento del compilador -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 lo amplía y agrega soporte a las anotaciones JAX-RS @XxxParam
con el JerseyParameterNameProvider
que también debería funcionar con otras implementaciones de 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();
}
}
Si no usa Dropwizard, puede usar el código anterior para crear su propia implementación.
La personalización del Validator
utilizado en la validación de las clases / métodos de recursos de Jersey se puede hacer usando la clase ValidationConfig
y exponiéndola a través del 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;
}
}
Luego registre ValidationConfigurationContextResolver
en ResourceConfig
.
Consulte la documentación de Jersey sobre el soporte de validación de frijoles para obtener más detalles.