quarkus icon indicating copy to clipboard operation
quarkus copied to clipboard

REST Client `@RestForm List<T>` inside of a `@BeanParam` bean does not convert

Open krnhotwings opened this issue 9 months ago • 1 comments

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&param=b&param=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

krnhotwings avatar Apr 27 '24 02:04 krnhotwings

/cc @cescoffier (rest-client), @geoand (rest-client)

quarkus-bot[bot] avatar Apr 27 '24 02:04 quarkus-bot[bot]

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

gian1200 avatar May 05 '24 16:05 gian1200

I finally got around to fixing the original issue.

geoand avatar May 15 '24 09:05 geoand