在Spring Webflux功能应用程序中验证请求的最佳方法是什么

问题描述 投票:4回答:2

在传统的Web应用程序中,很容易在控制器方法中验证请求主体,例如。

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

如果它是MVC应用程序,我们可以通过注入BindingResult来收集错误,并确定输入表单中是否存在某些验证错误。

在页面中,Freemarker和Thymeleaf存在一些助手来显示消息。

但是当我来到Webflux并尝试使用RouterFunction来定义应用程序中的路由时。例如,

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

一种可能的方法是将请求数据(MonoFlux)转换为阻塞和注入Validator并手动验证它们。

但我认为这些代码看起来有点难看。

如何优雅地处理请求正文或表单数据的验证?

是否有更好的验证请求正文或表单数据,并且不会丢失WEB(呈现视图)和REST应用程序的功能和反应功能?

spring functional-programming bean-validation project-reactor spring-webflux
2个回答
2
投票

我在我的应用程序中设法完成它的方法之一是(代码在Kotlin中,但想法是相同的)。我已经声明执行验证的RequestHandler类:

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

请求对象可以通过以下方式包含java验证注释:

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

处理程序类使用RequestHandler执行验证:

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

从这个blog post得到了主意。


2
投票

为此,我开发了“又一个验证器”。

https://github.com/making/yavi

如果YAVI能满足您的期望,那将是很棒的。

验证码如下所示:

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))));
}
© www.soinside.com 2019 - 2024. All rights reserved.