【问题标题】:REST request argument Validation In Router Function路由器功能中的 REST 请求参数验证
【发布时间】:2017-11-17 13:56:49
【问题描述】:

在 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

【问题讨论】:

    标签: spring spring-boot spring-webflux


    【解决方案1】:

    有点恼人的是,这个有用的功能似乎没有被带到功能世界,但自己实现验证步骤真的不是太难。方法如下。

    创建一个 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的功能。

    【讨论】:

    • 嗨,这应该是被接受的回复,你救了我的一天。谢谢
    【解决方案2】:

    您不能在(功能性)Spring Webflux 中使用基于注释的验证。见this answer

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

    【讨论】:

    • 更正:具有 Webflux 功能。带有注释模式的 Webflux 支持基于注释的验证。
    • @BrianClozel 哎呀!你当然是对的。我写得太快了。
    【解决方案3】:

    我创建了 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
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-13
      • 1970-01-01
      • 2017-03-13
      • 2019-02-23
      • 2018-03-23
      • 2017-10-21
      • 2015-09-08
      • 1970-01-01
      相关资源
      最近更新 更多