Bean validation in WebFlux does not work when using BindingResult in the controller's action
See original GitHub issueI’ve tried to use bean validation in Spring WebFlux but the request fails somewhere in the Netty pipeline if I specifiy “BindingResult bindingResult” parameter in the controller’s action.
The very same code works fine with normal Spring Mvc. Attached a sample project here.
Repro steps:
- just send a POST request to http://localhost:8080/ with request’s “Content-Type: application/json” header and let the payload be empty json ({}) --> the exception is thrown.
This is the stacktrace:
java.lang.IllegalStateException: Failed to resolve argument 1 of type 'org.springframework.validation.BindingResult' on public com.example.demojavawebflux.Movie com.example.demojavawebflux.DemoJavaWebfluxApplication.post(com.example.demojavawebflux.Movie,org.springframework.validation.BindingResult)
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.getArgumentError(InvocableHandlerMethod.java:228) ~[spring-webflux-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.resolveArg(InvocableHandlerMethod.java:223) ~[spring-webflux-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.lambda$null$1(InvocableHandlerMethod.java:179) ~[spring-webflux-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at java.util.Optional.orElseGet(Optional.java:267) ~[na:1.8.0_152]
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.lambda$resolveArguments$2(InvocableHandlerMethod.java:177) ~[spring-webflux-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_152]
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) ~[na:1.8.0_152]
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) ~[na:1.8.0_152]
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) ~[na:1.8.0_152]
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) ~[na:1.8.0_152]
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_152]
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) ~[na:1.8.0_152]
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.resolveArguments(InvocableHandlerMethod.java:183) ~[spring-webflux-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:136) ~[spring-webflux-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter.lambda$handle$1(RequestMappingHandlerAdapter.java:196) ~[spring-webflux-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:148) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoPeekFuseable.subscribe(MonoPeekFuseable.java:74) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoPeekFuseable.subscribe(MonoPeekFuseable.java:74) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:76) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:271) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:798) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:115) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:1649) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:156) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:1463) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:1337) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoMapFuseable.subscribe(MonoMapFuseable.java:59) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.Mono.subscribe(Mono.java:2913) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:418) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:210) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:91) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:55) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.FluxConcatMap.subscribe(FluxConcatMap.java:121) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoNext.subscribe(MonoNext.java:40) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoSwitchIfEmpty.subscribe(MonoSwitchIfEmpty.java:44) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.Mono.subscribe(Mono.java:2913) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:167) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.core.publisher.MonoPeekTerminal.subscribe(MonoPeekTerminal.java:61) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at reactor.ipc.netty.channel.ChannelOperations.applyHandler(ChannelOperations.java:388) ~[reactor-netty-0.7.1.RELEASE.jar:0.7.1.RELEASE]
at reactor.ipc.netty.http.server.HttpServerOperations.onHandlerStart(HttpServerOperations.java:359) ~[reactor-netty-0.7.1.RELEASE.jar:0.7.1.RELEASE]
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:163) ~[netty-common-4.1.16.Final.jar:4.1.16.Final]
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java) ~[netty-common-4.1.16.Final.jar:4.1.16.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:403) ~[netty-common-4.1.16.Final.jar:4.1.16.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463) ~[netty-transport-4.1.16.Final.jar:4.1.16.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858) ~[netty-common-4.1.16.Final.jar:4.1.16.Final]
at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_152]
Caused by: java.lang.NullPointerException: null
at org.springframework.web.reactive.result.method.annotation.ErrorsMethodArgumentResolver.resolveArgument(ErrorsMethodArgumentResolver.java:63) ~[spring-webflux-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.resolveArg(InvocableHandlerMethod.java:214) ~[spring-webflux-5.0.1.RELEASE.jar:5.0.1.RELEASE]
... 58 common frames omitted
[demo-java-webflux.zip](https://github.com/spring-projects/spring-boot/files/1452182/demo-java-webflux.zip)
Issue Analytics
- State:
- Created 6 years ago
- Comments:8 (6 by maintainers)
Top Results From Across the Web
Spring webflux bean validation not working - Stack Overflow
When the method begins you can do something like bindingResult.isValid() . We use an aspect over all controller methods to return an error ......
Read more >5. Validation, Data Binding, and Type Conversion - Spring
Spring features a Validator interface that you can use to validate objects. The Validator interface works using an Errors object so that while...
Read more >Spring Validation Example - Spring MVC Form Validator
Once this method returns, spring framework binds the Errors object to the BindingResult object that we use in our controller handler method.
Read more >Spring boot - Handling validation errors in RESTful API
So when the id is not present or have another value type than Integer, Spring will return a 400 Bad Request back to...
Read more >Unable to display error on JSP in Spring MVC validation
Using redirect will reset all the errors from BindingResult. You need not to use redirect. Try as if(result.hasErrors()) {. return new ...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
BindingResultafter@RequestBodyis currently not supported in WebFlux so that’s a feature request. Separately we do need to fix the NPE inErrorsMethodArgumentResolverwhich is a bug.Validation should still work, i.e. if the
BindingResultargument is removed I would expect aWebExchangeBindExceptionand a 400 response.@easmith please use StackOverflow for questions.