我目前正在研究使用休眠验证器支持使用 JSR 303 注释进行方法验证。最初的尝试尝试使用执行验证的代理(使用 cglib 生成)包装资源,但是,这似乎已经走到了尽头,因为我尝试过的代理方法似乎没有复制参数注释,所以依赖于此的资源方法最终会在没有参数的情况下被调用。我目前对该项目还有另一个问题:
java - 如何在java中创建一个保留方法参数注释的动态代理?
是否有另一种机制可以在不使用代理的情况下挂钩到 wink 的请求链来执行类似的操作?
我目前正在研究使用休眠验证器支持使用 JSR 303 注释进行方法验证。最初的尝试尝试使用执行验证的代理(使用 cglib 生成)包装资源,但是,这似乎已经走到了尽头,因为我尝试过的代理方法似乎没有复制参数注释,所以依赖于此的资源方法最终会在没有参数的情况下被调用。我目前对该项目还有另一个问题:
java - 如何在java中创建一个保留方法参数注释的动态代理?
是否有另一种机制可以在不使用代理的情况下挂钩到 wink 的请求链来执行类似的操作?
您可以通过创建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 的实现细节来获取实例、参数和元数据(如果他们提供了一种公共方式来获取这些信息,那就太好了)。但是,它比使用代理更可取,因为您不会从代理发生的反射中推断出多个开销。
通过添加 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 可以在这里提供帮助)。