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