What is the best way to validate request in a Spring Webflux functional application

前端 未结 2 769
天涯浪人
天涯浪人 2021-01-12 07:11

In a traditional web application it is easy to validate the request body in the controller method, eg.

ResponseEntity create(@Valid @ResponseBody Post post)         


        
相关标签:
2条回答
  • 2021-01-12 08:04

    I've developed "Yet Another Validator" for this porpose.

    https://github.com/making/yavi

    It would be great if YAVI could meet your expectation.

    Validation code will look like following:

    static RouterFunction<ServerResponse> routes() {
        return route(POST("/"), req -> req.bodyToMono(User.class) //
                .flatMap(body -> validator.validateToEither(body) //
                        .leftMap(violations -> {
                            Map<String, Object> error = new LinkedHashMap<>();
                            error.put("message", "Invalid request body");
                            error.put("details", violations.details());
                            return error;
                        })
                        .fold(error -> badRequest().syncBody(error), //
                              user -> ok().syncBody(user))));
    }
    
    0 讨论(0)
  • 2021-01-12 08:05

    One of the ways I've managed to do it in my application is the following (code is in Kotlin but the idea is the same). I've declared RequestHandler class which performs validation:

    @Component
    class RequestHandler(private val validator: Validator) {
    
        fun <BODY> withValidBody(
                block: (Mono<BODY>) -> Mono<ServerResponse>,
                request: ServerRequest, bodyClass: Class<BODY>): Mono<ServerResponse> {
    
            return request
                    .bodyToMono(bodyClass)
                    .flatMap { body ->
                        val violations = validator.validate(body)
                        if (violations.isEmpty())
                            block.invoke(Mono.just(body))
                        else
                            throw ConstraintViolationException(violations)
                    }
        }
    }
    

    Request objects can contain java validation annotations in this way:

    data class TokenRequest constructor(@get:NotBlank val accessToken: String) {
        constructor() : this("")
    }
    

    And handler classes use RequestHandler to perform validation:

    fun process(request: ServerRequest): Mono<ServerResponse> {
        return requestHandler.withValidBody({
            tokenRequest -> tokenRequest
                    .flatMap { token -> tokenService.process(token.accessToken) }
                    .map { result -> TokenResponse(result) }
                    .flatMap { ServerResponse.ok()
                            .contentType(MediaType.APPLICATION_JSON_UTF8)
                            .body(Mono.just(it), TokenResponse::class.java)
                    }
        }, request, TokenRequest::class.java)
    }
    

    Got the idea from this blog post.

    0 讨论(0)
提交回复
热议问题