我有一个控制器,它有一个post调用,接收的不是POJO,而是HttpServlet请求和@ParamBody body a la:
class MyController {
@PostMapping("/entrypoint")
public ResponseEntity<String> hello(HttpServletRequest request, @ParamBody body){
/* Many subcalls */
}
控制器将调用一些子调用到服务和其他rest客户端以形成响应,但我们在中间进行了许多验证,我们希望将它们移动到验证程序以进行一些订单。在主要子呼叫之前,有一个对小型外部服务的调用,根据响应,我们将考虑请求是否有效。我们还将把这个子调用移到验证器中,并将服务添加为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。
第一个问题:考虑到签名中有一个未注释的HttpServlet请求、一个@ParamBody主体和一个@Param参数,是否可以为我的示例做一个跨参数验证器?
第二:如果是,那么为什么不调用isValid?我需要更改什么?
第三:在控制器中添加@Validation是否将我的跨参数验证器转换为类验证器?
第四:如果我必须使用类验证器,因为我不能使用跨参数验证器:我如何读取ConstraintViolationException?ControllerHandler是否足以捕获异常?我如何填充ConstraintViolationException来知道我应该向客户端发送哪个错误响应?
它有点冷,因为这可能是一个角落的案件。我在某个地方读到过,但没有任何来源证明跨参数验证只适用于@Param,而不适用于@ParamBody,后者将由另一个组件管理,可能不接受将其与不同类型的参数一起放入isValid方法中。
我希望有人真的能帮忙。Ping我以获得任何澄清
在我看来,您应该创建一个DTO来接收有效负载,并创建annotation
来验证基于另一个的属性,然后您应该能够注入bindingresults
,其中包括验证错误(如果存在(,或者您可以在控制器方法中进一步实现自己的验证逻辑。