Bean validation in WebFlux does not work when using BindingResult in the controller's action

See original GitHub issue

I’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:

  1. 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:closed
  • Created 6 years ago
  • Comments:8 (6 by maintainers)

github_iconTop GitHub Comments

2reactions
rstoyanchevcommented, Nov 9, 2017

BindingResult after @RequestBody is currently not supported in WebFlux so that’s a feature request. Separately we do need to fix the NPE in ErrorsMethodArgumentResolver which is a bug.

Validation should still work, i.e. if the BindingResult argument is removed I would expect a WebExchangeBindException and a 400 response.

1reaction
bclozelcommented, Mar 21, 2018

@easmith please use StackOverflow for questions.

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found