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

本小妞迷上赌 提交于 2020-01-30 04:51:52

问题


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

ResponseEntity create(@Valid @ResponseBody Post post) {
} 

If it is a MVC application, we can gather the errors by injecting a BindingResult, and decide if there is some validation errors from the input form.

In the pages, there are some helpers existed for Freemarker and Thymeleaf to display the messages.

But when I come to Webflux and try to use RouterFunction to define the routing in the applications. For example,

Mono<ServerResponse> create(ServerRequest req) {
    return req.bodyToMono(Post.class)
    .flatMap { this.posts.save(it) }
    .flatMap { ServerResponse.created(URI.create("/posts/".concat(it.getId()))).build() }
}

@Bean
RouterFunction<ServerResponse> routes(PostHandler postController) {
    return route(GET("/posts"), postController.&all)
    .andRoute(POST("/posts"), postController.&create)
    .andRoute(GET("/posts/{id}"), postController.&get)
    .andRoute(PUT("/posts/{id}"), postController.&update)
    .andRoute(DELETE("/posts/{id}"), postController.&delete)
}

A possible approach is converting the request data(Mono or Flux) to blocking and injecting a Validator and validate them manually.

But I think the codes will look a little ugly.

How to process the validation of request body or form data gracefully?

Is there a better to validate the request body or form data and do not lost the functional and reactive features for both WEB(rendering a view) and REST applications?


回答1:


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.




回答2:


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))));
}


来源:https://stackoverflow.com/questions/48031647/what-is-the-best-way-to-validate-request-in-a-spring-webflux-functional-applicat

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!