quarkus
quarkus copied to clipboard
REST Client `@RestForm List<T>` inside of a `@BeanParam` bean does not convert
Describe the bug
Per https://github.com/quarkusio/quarkus/issues/39996#issuecomment-2078873768 filing another issue.
If a REST Client endpoint takes a @BeanParam
and the bean contains a @RestForm List<T>
field, quarkus will throw a runtime exception.
Expected behavior
The underlying HTTP request should be able to convert the List
into repeated form params, e.g. param=a¶m=b¶m=c
Actual behavior
When the REST client's endpoint is invoked, a runtime exception will be thrown:
2024-04-26 19:31:06,761 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /hello failed, error id: ab7ee47b-2627-4407-9500-6d947ea4a17f-1: java.lang.IllegalStateException: Form element 'org.acme.MyBean.params' could not be converted to 'String' for REST Client interface 'org.acme.GreetingClient'. A proper implementation of 'jakarta.ws.rs.ext.ParamConverter' needs to be returned by a 'jakarta.ws.rs.ext.ParamConverterProvider' that is registered with the client via the @RegisterProvider annotation on the REST Client interface.
at org.acme.GreetingClient$$QuarkusRestClientInterface.post(Unknown Source)
at org.acme.GreetingClient$$CDIWrapper.post(Unknown Source)
at org.acme.GreetingClient$$CDIWrapper_ClientProxy.post(Unknown Source)
at org.acme.GreetingResource.hello(GreetingResource.java:24)
at org.acme.GreetingResource$quarkusrestinvoker$hello_2615aa2773bd5be3c69078450e792903e1eacee1.invoke(Unknown Source)
at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:599)
at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2516)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2495)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1521)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:1583)
How to Reproduce?
Demo repo is here:
https://github.com/krnhotwings/quarkus-rest-client-beanparam-restform-list
Output of uname -a
or ver
6.8.7-300.fc40.x86_64
Output of java -version
OpenJDK Runtime Environment GraalVM CE 21.0.2+13.1
Quarkus version or git rev
3.10.0.CR1
Build tool (ie. output of mvnw --version
or gradlew --version
)
Gradle 8.7
Additional information
No response
/cc @cescoffier (rest-client), @geoand (rest-client)
Not sure if this might be tangentially related to a similar issue I'm facing. If not, I can create a separate issue.
Whenever I add @RestForm
to Map<String, String>
or MultivaluedMap<String, String>
params in a Rest Client method, I get the following error. Even when no value is passed:
java.lang.IllegalStateException: Form element '<redacted>.MicrosoftIdentityService.getToken' could not be converted to 'String' for REST Client interface '<redacted>.MicrosoftIdentityService'. A proper implementation of 'jakarta.ws.rs.ext.ParamConverter' needs to be returned by a 'jakarta.ws.rs.ext.ParamConverterProvider' that is registered with the client via the @RegisterProvider annotation on the REST Client interface.
// Doesn't work
TokenResponse getToken(@PathParam("tenant") String tenant,
@RestForm Map<String, String> form);
// Doesn't work
TokenResponse getToken(@PathParam("tenant") String tenant,
@RestForm MultivaluedMap<String, String> form);
// Works
TokenResponse getToken(@PathParam("tenant") String tenant,
@FormParam("client_id") String clientId,
@FormParam("username") String username,
@FormParam("password") String grantType,
@FormParam("client_secret") String clientSecret);
// Doesn't work
TokenResponse getToken(@PathParam("tenant") String tenant,
@FormParam("client_id") String clientId,
@FormParam("username") String username,
@FormParam("password") String grantType,
@FormParam("client_secret") String clientSecret,
@RestForm Map<String, String> filter); //empty map
I finally got around to fixing the original issue.