2014-03-19 13 views
21

如果您熟悉Bean Validation Framework,那麼您知道無法獲取方法參數的名稱。因此,如果對方法的第一個參數執行@NotNull約束,並且驗證失敗,則getPropertyPath將類似於「arg1」。我可以更改Method參數的ConstraintValidator中的屬性路徑嗎?

我想創建我自己的@NotNull版本, @NamedNotNull(「emailAddress」)。但我無法弄清楚如何覆蓋我的Validator中的#getPropertyPath?有沒有辦法做到這一點還是我堅持「ARG1」或「ARG2」等

編輯

基於我收到我能想出以下實現的回答允許我從@QueryParam或@PathParam註釋中獲取值,並將它們用作Bean Validation註釋的屬性路徑(如@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) 
{ ... } 
+0

如果不使用澤西島怎麼辦? – Dejell

+1

非常有用,謝謝。我添加了一些細化: '; if(name == null)name =「payload [」+ parameterTypes [index] .getSimpleName()+「]」;' ,而不是'「arg 」'。 這提供了有效負載的類型,在Rest方法中沒有檢查註釋的參數。當驗證有效載荷DTO的字段(以及有效載荷的類型(如果它完全缺失並具有@NotNull註釋)時,這會給出''路徑「:」SomeResource.testValidation.payload [PayloadDto] .attributes「。 – PhiLho

回答

1

Bean驗證1.1引入了ParameterNameProvider界面,用於創建違反約束的對象時方法和構造函數的參數提供的名稱。


Hibernate驗證5.2引入了ReflectionParameterNameProvider類,使用反射來獲取實際參數名稱的ParameterNameProvider實現(正常工作,它需要的類與-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實現工作過:

/** 
* 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,你可以使用上面的代碼來創建你自己的實現。


在Jersey資源類的驗證中使用的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; 
    } 
} 

然後寄存器ResourceConfigValidationConfigurationContextResolver

有關更多詳細信息,請參閱Jersey documentation about Bean Validation support

10

如果你熟悉Bean驗證框架,你知道 你不能讓一個方法參數

這是不完全正確的名稱。 Bean Validation指定了一個ParameterNameProvider的概念,它允許您提供自己的實現。 Hibernate Validator與ParaNamer集成以提供參數名稱。有關更多信息,請參見Validator online docs。一旦Validator支持Java 8,它也將支持Java 8參數命名工具。

海事組織,你應該給ParaNamer一展身手。

+0

好的,我很高興嘗試這個。我在JAX-RS上使用它,所以我通常可以從QueryParam或PathParam註釋中獲取參數名稱!如果這項工作,我一定會選擇你的答案。 –

+0

它@Hardy!我將用一個例子編輯你的回答,並將其標記爲已回答。 –

+0

http://paranamer.codehaus.org/不再是有效的鏈接 – Dejell

相關問題