3

我目前正在研究使用hibernate驗證器支持使用JSR 303批註進行方法驗證。最初的嘗試嘗試使用執行驗證的代理(使用cglib生成)來封裝資源,但是,由於我嘗試的代理方法似乎沒有複製參數註釋,因此這看起來已經到了死衚衕,所以依賴於這個的資源方法最終被不帶參數地調用。我對這個項目目前開放了另一個問題:我是否可以使用apache wink(JAX/RS)實現Hibernate(JSR 303)方法驗證

How can I create a dynamic proxy in java that retains parameter annotations on methods?

是否有其他的機制掛鉤到眨眼的請求鏈,做這樣的事情,而不使用代理?

回答

4

通過創建RequestHandler並重寫實際的HandlersFactory以返回包含請求處理程序的列表,可以執行部分​​支持的操作。這個配置討論here。這個請求處理程序將直接在InvokeMethodHandler(它是最後一個請求處理程序調用的,這是實際調用資源方法的那個處理程序)之前插入到請求處理鏈中。

基於閱讀的InvokeMethodHandler的源代碼(這實際上調用你的JAX/RS資源),你可以得到的參數,實例和方法參數如下:

// Get Method Validator from hibernate 
    MethodValidator validator = Validation.byProvider(HibernateValidator.class).configure() 
      .buildValidatorFactory().getValidator().unwrap(
        MethodValidator.class); 

    // Extract the method parameters, object instance and method metadata from the JAX/RS internals. 
    Method javaMethod = null; 
    Object instance = null; 
    Object[] parameters = null; 
    SearchResult searchResult = context.getAttribute(SearchResult.class); 

    javaMethod = searchResult.getMethod().getMetadata() 
      .getReflectionMethod(); 

    parameters = searchResult.getInvocationParameters(); 
    instance = searchResult.getResource().getInstance(context); 

    // Use all this to perform validation... 
    Set<MethodConstraintViolation<Object>> violations = validator 
      .validateAllParameters(instance, javaMethod, parameters); 
    if (!violations.isEmpty()) { 
     // do something with the violations here 
    } 

這有點哈克,如它依賴(據我所知可以沒有記錄)wink的實現細節來獲取實例,參數和元數據(如果它們提供公開的方式來獲取此信息,那將是很好的)。但是,使用代理比使用代理更有優勢,因爲您不會從代理髮生的反射中推斷出多重開銷。

2

我設法通過添加EJB攔截器來執行wink的bean驗證。在這裏,我用OpenJPA的驗證作爲一個供應商:

@Interceptor 
public class ValidationInterceptor { 

    Validator validator = Validation.byProvider(ApacheValidationProvider.class).configure().buildValidatorFactory().getValidator(); 

    @AroundInvoke 
    public Object validate(InvocationContext ctx) throws Exception { 
     Object[] parameters = ctx.getParameters(); 
     Set<ConstraintViolation<Object>> validateResult = new HashSet<>(); 

     for (Object parameter : parameters) { 
      Set<ConstraintViolation<Object>> validateParam = validator.validate(parameter); 
      validateResult.addAll(validateParam); 
     } 
     if (!validateResult.isEmpty()) { 
      throw new RestValidationException(validateResult); 
     } 
     return ctx.proceed(); 
    } 
} 

自定義異常是相當輸出:

public class RestValidationException extends WebApplicationException { 

    List<String> validationErrors = new ArrayList<String>(); 

    public RestValidationException(Set<? extends ConstraintViolation<?>> violations) { 
     for(ConstraintViolation<?> constraintViolation : violations) { 
      String error = constraintViolation.getPropertyPath().toString() + ": " + constraintViolation.getMessage(); 
      validationErrors.add(error); 
     } 
    } 

    @Override 
    public Response getResponse() { 
     return Response.status(Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(validationErrors).build(); 
    } 
} 

現在你有@Interceptors(ValidationInterceptor.class)和所有的參數都標註您的REST資源在進入方法之前進行驗證。 如果你不使用EJB,你可以用cglib的MethodInterceptor攔截它(Spring可以在這裏獲得幫助)。