【发布时间】:2021-07-19 09:11:04
【问题描述】:
我有一个带有 post call 的控制器,它接收的不是 POJO,而是一个 HttpServletRequest 请求和一个 @ParamBody 正文:
class MyController {
@PostMapping("/entrypoint")
public ResponseEntity<String> hello(HttpServletRequest request, @ParamBody body){
/* Many subcalls */
}
控制器将调用一些对服务和其他休息客户端的子调用以形成响应,但我们在中间完成了许多验证,我们希望将它们移动到验证器以进行排序。在主子调用之前,会调用一个小型外部服务,根据响应我们将考虑请求是否有效。我们也会将该子调用移至验证器,并将服务添加为 bean。稍后我们需要在控制器中使用来自该调用的响应。
为此,我创建了一个验证器及其接口:
@Constraint(validatedBy = ParamConstraintValidator.class)
@Target({ PARAMETERS })
@Retention(RUNTIME)
@Documented
public @interface ParamConstraint{
String message() default "default message";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class ParamConstraintValidator implements
ConstraintValidator<ParamConstraint, Object[]> {
@Autowire
ClientCheck clientCheck;
@Override
public void initialize(ParamConstraint paramConstraint) {
}
@Override
public boolean isValid(Object[] value, ConstraintValidatorContext context) {
if ( value.length != 4 ) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("Param number is wrong")
.addConstraintViolation();
return false;
}
if ( value[0] == null || value[1] == null ) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("Param contents are null")
.addConstraintViolation();
return false;
}
/* More validations*/
ClientToken ct = clientCheck.verify(value[0], value[1])
/* More validations*/
value[2] = ct;
return true;
}
由于我们需要将该客户端令牌传递给后续子调用,因此我已将其作为第三个参数添加到控制器的方法签名中(第四个参数是 BindingResult 以从上下文中检索错误)
class MyController {
@PostMapping("/entrypoint")
@ParamConstraint
public ResponseEntity<String> hello(HttpServletRequest request, @ParamBody String body,
@Param(required = false) ClientToken ct, BindingResult result){
try {
if(result.hasErrors()) {
throw new WhateverException("Custom error to be processed by a handler");
}
/* Many subcalls */
}
catch (Exception e) {
}
}
我注意到的第一件事是验证器中的 isValid 没有被调用。经过多次试验和错误后,我尝试将@Validation 注解添加到控制器类中,如下所示:
@Validation
MyController
只有这样 isValid 方法才开始起作用。我可以从 isValid 正确获取 clientToken。当我必须在 isValid 中返回 false 时,问题就来了。接下来发生的事情是没有我所期望的 ParameterException,而是一个我无法在控制器中捕获的 ConstraintViolationException。 BindingResult 似乎无法使用,所以我也不知道触发异常的错误是什么。看起来理论上的跨参数约束被转换为阻止我从控制器捕获 ViolationConstraintException 的类约束。
第一个问题:考虑到我在签名中有一个未注释的 HttpServletRequest、一个@ParamBody 正文和一个@Param 参数,是否可以为我的示例做一个跨参数验证器?
第二:如果是,为什么不调用 isValid 呢?我需要改变什么?
第三:在控制器中添加@Validation 是否会将我的跨参数验证器转换为一个类?
第四:如果我必须使用类验证器,因为我不能使用跨参数验证器:如何读取 ConstraintViolationException? ControllerHandler 是否足以捕获异常?如何填充 ConstraintViolationException 以了解应将哪个错误响应发送回客户端?
它有点长,因为这可能是一个极端情况。我在某处读过但没有来源证明跨参数验证仅适用于 @Param 但不适用于 @ParamBody 它将由不同的组件管理,可能不接受将其放入不同类型的 isValid 方法中参数。
我希望有人能真正提供帮助。联系我以获得任何澄清
【问题讨论】:
-
用几乎完全相同的实现来面对这个问题。你有没有设法摆脱它。
标签: java spring spring-boot validation spring-mvc