web-dev-qa-db-ja.com

SpringWebflux機能アプリケーションでリクエストを検証するための最良の方法は何ですか

従来の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)
}

考えられるアプローチは、リクエストデータ(MonoまたはFlux)をブロックしてValidatorを挿入し、手動で検証することです。

しかし、コードは少し醜いように見えると思います。

リクエスト本文またはフォームデータの検証を処理する方法優雅に

リクエストの本文またはフォームデータを検証し、WEB(ビューのレンダリング)とRESTアプリケーションの両方の機能的および反応的な機能を失わないようにする方が良いですか?

7
Hantsy

私はこの目的のために「YetAnotherValidator」を開発しました。

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))));
}
3
Toshiaki Maki

私が自分のアプリケーションでそれを行うことができた方法の1つは、次のとおりです(コードは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)
}

これからアイデアを得ました ブログ投稿

2
Yuriy Yunikov