Search code examples
javajsonjerseymultipartform-datajersey-2.0

Jersey2 supporting both APPLICATION_JSON and MULTIPART_FORM_DATA fails to accept JSON input


I'm new to Jersey and trying to implement a POST endpoint which could accept both application/json and multipart/form-data content-type, separately.

    @POST
    @Path("/upload")
    @Consumes({MediaType.APPLICATION_JSON, MediaType.MULTIPART_FORM_DATA})
    @Produces({MediaType.APPLICATION_JSON, MediaType.MULTIPART_FORM_DATA})
    public void upload(@FormDataParam("jsonInput") MyPojo req,
            @FormDataParam("file") FormDataBodyPart file)
    {
        //...
    }

Below are the Spring Boot dependencies:

    dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jersey</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-multipart</artifactId>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
    </dependency>

I have the necessary resources configured by extending ResourceConfig and registering the my REST API classes also included few standard ones like below:

    register(MyRestImpl.class);
    register(JacksonFeature.class);
    register(MultiPartFeature.class);

"jersey-media-json-jackson" is already added in Maven dependency which should take care of serialize/deserialize POJOs.

Now, I see sending JSON input as part of Multipart request, works fine. Whereas, sending just the same JSON input as Content-Type: application/json fails to even hit the service and Postman reports below deserializing error:

Cannot find a deserializer for non-concrete Map type [map type; class javax.ws.rs.core.MultivaluedMap, [simple type, class java.lang.String] -> [collection type; class java.util.List, contains [simple type, class java.lang.String]]]
 at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 1, column: 1]

Why sending JSON data as application/json Content-Type does not work and same JSON data sent as multipart/form-data Content-Type works? I do not think, we need to annotate anything special for this, am I right?


Solution

  • Accepting both multipart and JSON in the same method is not going to work. The reason is that they cannot be correctly deserialized into the same objects and different number of parameters and annotations meant to support multipart. What you can do though is have two methods (to the same URL), each with a different media type.

    @POST
    @Path("/upload")
    @Consumes({MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_JSON})
    public void json(JsonPojo pojo) { }
    
    @POST
    @Path("/upload")
    @Consumes({MediaType.MULTIPART_FORM_DATA})
    @Produces({MediaType.APPLICATION_JSON})
    public void upload(@FormDataParam("jsonInput") MyPojo req,
                       @FormDataParam("file") FormDataBodyPart file) { }