3

我目前正在研究使用休眠验证器支持使用 JSR 303 注释进行方法验证。最初的尝试尝试使用执行验证的代理(使用 cglib 生成)包装资源,但是,这似乎已经走到了尽头,因为我尝试过的代理方法似乎没有复制参数注释,所以依赖于此的资源方法最终会在没有参数的情况下被调用。我目前对该项目还有另一个问题:

java - 如何在java中创建一个保留方法参数注释的动态代理?

是否有另一种机制可以在不使用代理的情况下挂钩到 wink 的请求链来执行类似的操作?

4

2 回答 2

4

您可以通过创建RequestHandler并覆盖实际的HandlersFactory以返回包含您的请求处理程序的列表,这是一种部分支持的方式。此处讨论了此配置。这个请求处理程序将直接插入到 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 
    }

这有点 hacky,因为它依赖(据我所知没有记录)wink 的实现细节来获取实例、参数和元数据(如果他们提供了一种公共方式来获取这些信息,那就太好了)。但是,它比使用代理更可取,因为您不会从代理发生的反射中推断出多个开销。

于 2011-08-09T14:31:40.203 回答
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 可以在这里提供帮助)。

于 2014-03-04T08:58:29.340 回答