従来の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アプリケーションの両方の機能的および反応的な機能を失わないようにする方が良いですか?
私はこの目的のために「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))));
}
私が自分のアプリケーションでそれを行うことができた方法の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)
}
これからアイデアを得ました ブログ投稿 。