2014-03-19 13 views
21

Wenn Sie mit dem Bean Validation Framework vertraut sind, wissen Sie, dass Sie den Namen eines Methodenarguments nicht ermitteln können. Wenn Sie also eine @NotNull-Einschränkung für das erste Argument einer Methode ausführen und die Validierung fehlschlägt, wird getPropertyPath so aussehen wie "arg1".Kann ich den Eigenschaftspfad in einem ConstraintValidator für Method-Argumente ändern?

Ich möchte meine eigene Version von @NotNull erstellen, die einen Wert z. @NamedNotNull ("E-Mail-Adresse"). Aber ich kann nicht herausfinden, wie man den #getPropertyPath in meinem Validator überschreibt? Gibt es eine Möglichkeit, dies zu tun, oder bin ich mit „arg1“ stecken oder „arg2“ usw.

EDIT

Basierend auf der Antwort, die ich erhielt ich konnte mit der folgenden Umsetzung zu entwickeln, die Erlaubt mir, den Wert aus meinen @QueryParam- oder @PathParam-Annotationen zu übernehmen und diese als Eigenschaftspfad für Bean-Validierungsanmerkungen wie @NotNull zu verwenden.

Für Jersey müssen Sie die folgende Klasse erstellen. Beachten Sie die Implementierung von DefaultParameterNameProvider:

register(ValidationConfigurationContextResolver.class); 

Das ist es:

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; 
     } 
    } 
} 

Dann in Ihrem RestConfig Sie die folgende Zeile hinzufügen müssen. Jetzt enthalten Ihre ConstraintValidationExceptions den Namen des QueryParam oder PathParam, mit dem sie kommentiert werden. Zum Beispiel:

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

was passiert, wenn man verwenden Jersey nicht? – Dejell

+1

Sehr nützlich, danke. Ich habe ein wenig Verfeinerung: 'if (name == null) name = "Nutzlast [" + parameterTypes [index] .getSimpleName() + "]";' statt ' "arg "'. Dies stellt die Art der Nutzlast, der Parameter ohne die inspizierten Annotationen im Verfahren Rest. Dies ergibt '" Pfad ":" SomeResource.testValidation.payload [PayloadDto] .attributes "' bei der Validierung der Felder des Payload-DTO (und des Typs der Payload, wenn sie vollständig fehlt und die Annotation @NotNull hat). – PhiLho

Antwort

1

Bean Validation 1.1 führte die ParameterNameProvider-Schnittstelle für die Bereitstellung von Namen für Methoden- und Konstruktorparameter beim Erstellen von Constraint-Schutzobjekten ein.


Hibernate Validator 5.2 führte die ReflectionParameterNameProvider Klasse, eine ParameterNameProvider Implementierung, die Reflexion verwendet die tatsächlichen Parameternamen zu erhalten (um richtig zu funktionieren, erfordert es die Klassen mit dem -parameters Compiler Argument kompiliert werden):

/** 
* 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 erweitert sie und fügen sie Unterstützung JAX-RS @XxxParam Anmerkungen mit dem JerseyParameterNameProvider, die auch mit anderen JAX-RS-Implementierungen funktionieren sollte:

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

Wenn Sie DropWizard nicht verwenden, können Sie den obigen Code verwenden, um Ihre eigene Implementierung zu erstellen.


Anpassung des verwendeten Validator bei der Validierung von Jersey Ressourcenklassen/Methoden ValidationConfig-Klasse und über ContextResolver<T> Mechanismus erfolgen kann aussetzt:

public class ValidationConfigurationContextResolver 
     implements ContextResolver<ValidationConfig> { 

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

dann die ValidationConfigurationContextResolver in ResourceConfig registrieren.

Siehe die Jersey documentation about Bean Validation support für weitere Details.

10

Wenn Sie mit dem Bean Validation Framework vertraut sind, wissen Sie, dass Sie nicht den Namen einer Methode Argument

Das ist nicht ganz richtig bekommen. Bean Validation gibt das Konzept eines ParameterNameProvider an, mit dem Sie Ihre eigene Implementierung bereitstellen können. Hibernate Validator integriert mit ParaNamer, um Parameternamen bereitzustellen. Weitere Informationen finden Sie im Validator online docs. Sobald Validator Java 8 unterstützt, unterstützt es auch die Java 8-Parameterbenennungsfunktion.

IMO, sollten Sie ParaNamer geben gehen.

+0

Okay, ich bin aufgeregt, das zu versuchen. Ich verwende diese mit JAX-RS so ich in der Regel die Parameternamen aus dem QueryParam oder PathParam Anmerkungen bekommen! Wenn das funktioniert, werde ich definitiv Ihre Antwort auswählen. –

+0

Es funktioniert @Hardy! Ich werde Ihre Antwort mit einem Beispiel bearbeiten und als beantwortet markieren. –

+0

http://paranamer.codehaus.org/ ist kein gültiger Link mehr – Dejell