REST请求参数验证在路由器功能中

时间:2017-11-17 13:56:49

标签: spring spring-boot spring-webflux

在Spring控制器方法中,我们可以使用@Valid和这样的东西进行REST请求参数验证

@PostMapping(REGISTER)
  public ResponseEntity<SomeData> registerSomeData(@RequestBody @Valid final SomeData someData) {
    ...................
  }



public class SomeData {

  @Size(min = 2, max = 20)
  private String firstname;

  @Digits(fraction = 0, integer = 10)
  private Integer customerID;

  @NotNull
  private Customer customer;
}

如果请求与这些约束不匹配,那么Spring框架将抛出​​Bad Request Exception(400)。

使用Spring5路由器功能,我不明白我们如何做到这一点,因为我们无法在路由器功能中提供@Valid

3 个答案:

答案 0 :(得分:1)

我创建了 GeneralValidator 类,它可以工作 javax.validation.Validator

@Component
@RequiredArgsConstructor
public class GeneralValidator {

    private final Validator validator;

    private <T> void validate(T obj) {

        if (obj == null) {
            throw new IllegalArgumentException();
        }

        Set<ConstraintViolation<T>> violations = this.validator.validate(obj);
        if (violations != null && !violations.isEmpty()) {
            throw new ConstraintViolationException(violations);
        }
    }


     /**
     * @param obj object we will validate
     * @param next Publisher we will call if don't have any validation hits
     */
    public <T> Mono<ServerResponse> validateAndNext(T obj, Mono<ServerResponse> next) {
        try {
            validate(obj);
            return next;
        } catch (IllegalArgumentException ex) {
            return ServerResponse.badRequest()
                    .body(new ErrorResponse("Request body is empty or unable to deserialize"), ErrorResponse.class);
        } catch (ConstraintViolationException ex) {
            return ServerResponse.badRequest()
                    .body(new ValidationErrorResponse(
                            "Request body failed validation",
                            ex.getConstraintViolations()
                                    .stream()
                                    .map(v -> "Field '%s' has value %s but %s"
                                            .formatted(v.getPropertyPath(), v.getInvalidValue(),v.getMessage()))
                                    .collect(Collectors.toList())
                    ), ValidationErrorResponse.class);
        }
    }
}

使用方法:

...
.POST("/", req -> req.bodyToMono(RequestObject.class)
        .flatMap(r -> validator.validateAndNext(r,routeFunction.execute(r)))
)
...

routeFunction.execute

public @NotNull Mono<ServerResponse> execute(RequestObject request) {
//handling body
}

答案 1 :(得分:0)

您无法使用(功能性)Spring Webflux进行基于注释的验证。请参阅this answer

如果你绝对需要基于注释的验证,你应该知道你可以继续使用传统的Spring MVC和Spring 5(或非功能性的Webflux)。

答案 2 :(得分:0)

有点恼人的是,这种有用的功能似乎还没有被带入功能世界,但是您自己实施验证步骤确实并不难。就是这样。

创建一个bean来执行验证:

@Component
public class RequestValidator {

  @Inject
  Validator validator;

  public <T> Mono<T> validate(T obj) {

    if (obj == null) {
      return Mono.error(new IllegalArgumentException());
    }

    Set<ConstraintViolation<T>> violations = this.validator.validate(obj);
    if (violations == null || violations.isEmpty()) {
      return Mono.just(obj);
    }

    return Mono.error(new ConstraintViolationException(violations));
  }
}

现在,您的处理程序函数中将包含执行验证的步骤。在此示例中,FindRequest类是JSON域模型类,其中包含诸如@NotEmpty@NotNull等验证批注。根据此虚拟示例,调整构造ServerResponse的方式调用反应式数据存储库。

  @Component
  public class MyHandler {

    @Inject
    RequestValidator validator;

    public Mono<ServerResponse> findAllPeople(ServerRequest request) {

      return request.bodyToMono(FindRequest.class)
          .flatMap(this.validator::validate)
          .flatMap(fr -> ServerResponse
              .ok()
              .body(this.repo.findAllByName(fr.getName()), Person.class));
    }
  }

可以使用相同的方法来扩展功能以处理FluxMono