quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.41k stars 2.57k forks source link

Multipart @FormParam on EntityPart not working according to Jakarta RESTful Web Services 3.1 #41186

Open ollelogdahl opened 1 month ago

ollelogdahl commented 1 month ago

Describe the bug

According to the Jakarta RESTful Web Services 3.1 Specification, @FormParam should be supported on EntityPart parameters in resources. More specifically stated:

For resource methods that consume multipart/form-data media types, the @FormParam annotation may be used on jakarta.ws.rs.core.EntityPart parameters. link

Currently, this is not supported in quarkus-rest (resteasy-reactive), and giving the error Could not create converter for jakarta.ws.rs.core.EntityPart. It is in-fact not possible to implement your own ParameterConverter, as the FormParamExtractor giving the string-value for the converter doesn't include the 'content-disposition' row.

The following is the jist of the reproducer (a full project is attached below):

@Path("/api")
public class GreetingResource {

    static final Logger LOG = LoggerFactory.getLogger(GreetingResource.class);

    @POST
    @Path("/upload")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces(MediaType.APPLICATION_JSON)
    public Response upload(@FormParam("part1") EntityPart part) {
        LOG.info("part: {}", part);
        return Response.ok().build();
    }
}

This should run correctly and respond with 200 using the following curl command

curl -vvv -X POST -F "part1=hello" http://localhost:8080/api/upload

This was previously reported in https://github.com/quarkusio/quarkus/issues/38980, but no reproducer was provided.

Expected behavior

The program should start up successfully and respond to the following curl with 200:

curl -vvv -X POST -F "part1=hello" http://localhost:8080/api/upload

The logs should print that the part is not null. Inspecting the EntityPart should give access to all attributes taken from the content-disposition header (e.g. filename), and the content should be "hello".

Actual behavior

Currently quarkus fails to start and logs the following:

[error]: Build step io.quarkus.resteasy.reactive.server.deployment.ResteasyReactiveProcessor#setupEndpoints threw an exception: java.lang.RuntimeException: java.lang.RuntimeException: Failed to process method 'org.acme.GreetingResource#upload'
    at org.jboss.resteasy.reactive.common.processor.EndpointIndexer.createEndpoints(EndpointIndexer.java:332)
    at io.quarkus.resteasy.reactive.server.deployment.ResteasyReactiveProcessor.setupEndpoints(ResteasyReactiveProcessor.java:663)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at io.quarkus.deployment.ExtensionLoader$3.execute(ExtensionLoader.java:849)
    at io.quarkus.builder.BuildContext.run(BuildContext.java:256)
    at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
    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 java.base/java.lang.Thread.run(Thread.java:1570)
    at org.jboss.threads.JBossThread.run(JBossThread.java:483)
Caused by: java.lang.RuntimeException: Failed to process method 'org.acme.GreetingResource#upload'
    at org.jboss.resteasy.reactive.common.processor.EndpointIndexer.createResourceMethod(EndpointIndexer.java:780)
    at org.jboss.resteasy.reactive.common.processor.EndpointIndexer.createEndpoints(EndpointIndexer.java:421)
    at org.jboss.resteasy.reactive.common.processor.EndpointIndexer.createEndpoints(EndpointIndexer.java:299)
    ... 11 more
Caused by: java.lang.RuntimeException: Could not create converter for jakarta.ws.rs.core.EntityPart for method jakarta.ws.rs.core.Response upload(jakarta.ws.rs.core.EntityPart part) on class org.acme.GreetingResource of type FORM
    at org.jboss.resteasy.reactive.server.processor.ServerEndpointIndexer.handleOtherParam(ServerEndpointIndexer.java:373)
    at org.jboss.resteasy.reactive.server.processor.ServerEndpointIndexer.handleOtherParam(ServerEndpointIndexer.java:98)
    at org.jboss.resteasy.reactive.common.processor.EndpointIndexer.extractParameterInfo(EndpointIndexer.java:1449)
    at org.jboss.resteasy.reactive.common.processor.EndpointIndexer.createResourceMethod(EndpointIndexer.java:598)
    ... 13 more
Caused by: java.lang.RuntimeException: Failed to find converter for jakarta.ws.rs.core.EntityPart
    at org.jboss.resteasy.reactive.server.processor.generation.converters.GeneratedConverterIndexerExtension.extractConverterImpl(GeneratedConverterIndexerExtension.java:106)
    at org.jboss.resteasy.reactive.server.processor.ServerEndpointIndexer.extractConverter(ServerEndpointIndexer.java:579)
    at org.jboss.resteasy.reactive.server.processor.ServerEndpointIndexer.handleOtherParam(ServerEndpointIndexer.java:369)
    ... 16 more

How to Reproduce?

  1. extract reproducer.zip.
  2. run ./mvnw clean quarkus:dev

If quarkus succeeds in starting up, you need to

  1. execute the following command and check the response code
    curl -vvv -X POST -F "part1=hello" http://localhost:8080/api/upload
  2. Check the application logs to see if the part was not null.

Output of uname -a or ver

Linux olle 4.18.0-553.5.1.el8_10.x86_64 #1 SMP Tue May 21 03:13:04 EDT 2024 x86_64 x86_64 x86_64 GNU/Linux

Output of java -version

openjdk version "22.0.1" 2024-04-16

Quarkus version or git rev

3.11.0

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.9.6 (bc0240f3c744dd6b6ec2920b3cd08dcc295161ae)

Additional information

No response

quarkus-bot[bot] commented 1 month ago

/cc @FroMage (resteasy-reactive), @stuartwdouglas (resteasy-reactive)

geoand commented 1 month ago

@FroMage loves these :)

FroMage commented 1 month ago

Well, there's a long list of things to improve with multipart. Do you feel like helping contribute support for this @ollelogdahl ?