After upgrading to Spring Boot 3.2.0-RC1 from 3.1.5 request params binding is not working.
@RestController
@RequestMapping
static class HelloController {
enum Tag {RED, GREEN}
record HelloArgs(String name, Set<Tag> tags) {
HelloArgs(@RequestParam String name, @RequestParam Set<Tag> tags) {
this.name = name;
this.tags = tags == null ? Set.of() : tags;
}
}
@GetMapping
String hello(HelloArgs args) {
return "Hello %s %s".formatted(args.name(), args.tags.stream().map(Tag::name).collect(Collectors.joining()));
}
}
With request:
http GET http://localhost:8080\?name\=Hulk
ERROR 54976 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.IllegalStateException: No primary or single unique constructor found for interface java.util.Set] with root cause
java.lang.IllegalStateException: No primary or single unique constructor found for interface java.util.Set
at org.springframework.beans.BeanUtils.getResolvableConstructor(BeanUtils.java:266) ~[spring-beans-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.validation.DataBinder.createObject(DataBinder.java:923) ~[spring-context-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.validation.DataBinder.createObject(DataBinder.java:952) ~[spring-context-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.validation.DataBinder.construct(DataBinder.java:902) ~[spring-context-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.bind.ServletRequestDataBinder.construct(ServletRequestDataBinder.java:112) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.constructAttribute(ServletModelAttributeMethodProcessor.java:156) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:148) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:217) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:170) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:918) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:830) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[tomcat-embed-core-10.1.15.jar:6.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.15.jar:6.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.15.jar:10.1.15]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
While with: http GET http://localhost:8080\?name\=Hulk\&tags\=RED,GREEN
everything is okay.
Comment From: rstoyanchev
Thanks for giving the RC2 a try.
Both Spring Framework 6.0.x (Boot 3.1.x) and 6.1 (Boot 3.2) do not support constructor binding to a Collection
. In the above example, 6.0.x passes null
for the second parameter. 6.1 supports nested data constructors, and fails because it's trying to do more. We can make 6.1 ignore collection parameters, and pass null
too, but I just confirm what your expectations are. Even in 6.0.x the tags
parameter is not supported, it just happens to be more lenient by passing null
.
As an aside, the @RequestParam
annotations don't play any role at that level, unless you have some custom support for them. They're only checked when declared on controller method parameters.
Comment From: burl21
Thanks for the tip @RequestParam
.
If 6.1 is more strict, how should tags be declared?
Comment From: rstoyanchev
It's not more strict intentionally. We can adjust the behavior to be equally lenient and pass null
. I just wasn't sure what role tags
plays in the first place, it's not very useful if it's always null
.