Skip to content

Commit

Permalink
Multipart download in REST Client Reactive
Browse files Browse the repository at this point in the history
refs #21440
  • Loading branch information
michalszynkiewicz committed Jan 13, 2022
1 parent 3491d27 commit 3453f79
Show file tree
Hide file tree
Showing 25 changed files with 2,818 additions and 59 deletions.
48 changes: 44 additions & 4 deletions docs/src/main/asciidoc/rest-client-reactive.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ public interface ExtensionsService {
// ...
@GET
Uni<Set<Extension>> getByIdAsUni(@QueryParam("id") String id)
Uni<Set<Extension>> getByIdAsUni(@QueryParam("id") String id);
}
----

Expand All @@ -470,7 +470,7 @@ import java.util.Set;
import java.util.concurrent.CompletionStage;
@Path("/extension")
public class ExtensionsResource
public class ExtensionsResource {
@RestClient
ExtensionsService extensionsService;
Expand Down Expand Up @@ -654,15 +654,19 @@ org.eclipse.microprofile.rest.client.propagateHeaders=Authorization,Proxy-Author

== Multipart Form support

Rest Client Reactive allows sending data as multipart forms. This way you can for example
REST Client Reactive support multipart messages.

=== Sending Multipart messages

REST Client Reactive allows sending data as multipart forms. This way you can for example
send files efficiently.

To send data as a multipart form, you need to create a class that would encapsulate all the fields
to be sent, e.g.

[source, java]
----
class FormDto {
public class FormDto {
@FormParam("file")
@PartType(MediaType.APPLICATION_OCTET_STREAM)
public File file;
Expand Down Expand Up @@ -697,6 +701,42 @@ by specifying `quarkus.rest-client.multipart-post-encoder-mode` in your
clients created with the `@RegisterRestClient` annotation.
All the available modes are described in the link:https://netty.io/4.1/api/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.EncoderMode.html[Netty documentation]

=== Receiving Multipart Messages
REST Client Reactive also supports receiving multipart messages.
As with sending, to parse a multipart response, you need to create a class that describes the response data, e.g.

[source,java]
----
public class FormDto {
@RestForm // <1>
@PartType(MediaType.APPLICATION_OCTET_STREAM)
public File file;
@FormParam("otherField") // <2>
@PartType(MediaType.TEXT_PLAIN)
public String textProperty;
}
----
<1> uses the shorthand `@RestForm` annotation to make a field as a part of a multipart form
<2> the standard `@FormParam` can also be used. It allows to override the name of the multipart part.

Then, create an interface method that corresponds to the call and make it return the `FormDto`:
[source,java]
----
@GET
@Produces(MediaType.MULTIPART_FORM_DATA)
@Path("/get-file")
FormDto data sendMultipart();
----

At the moment, multipart response support is subject to the following limitations:

- files sent in multipart responses can only be parsed to `File`, `Path` and `FileDownload`
- each field of the response type has to be annotated with `@PartType` - fields without this annotation are ignored

REST Client Reactive needs to know the classes used as multipart return types upfront. If you have an interface method that produces `multipart/form-data`, the return type will be discovered automatically. However, if you intend to use the `ClientBuilder` API to parse a response as multipart, you need to annotate your DTO class with `@MultipartForm`.

WARNING: The files you download are not automatically removed and can take up a lot of disk space. Consider removing the files when you are done working with them.

== Proxy support
REST Client Reactive supports sending requests through a proxy.
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package io.quarkus.jaxrs.client.reactive.runtime;

import java.util.Map;
import java.util.function.Supplier;

import org.jboss.resteasy.reactive.client.impl.ClientProxies;
import org.jboss.resteasy.reactive.client.impl.DefaultClientContext;
import org.jboss.resteasy.reactive.client.spi.ClientContext;
import org.jboss.resteasy.reactive.client.spi.ClientContextResolver;
import org.jboss.resteasy.reactive.client.spi.MultipartResponseData;
import org.jboss.resteasy.reactive.common.core.GenericTypeMapping;
import org.jboss.resteasy.reactive.common.core.Serialisers;

Expand Down Expand Up @@ -51,6 +53,14 @@ public ClientProxies getClientProxies() {
}
return clientProxies;
}

@Override
public Map<Class<?>, MultipartResponseData> getMultipartResponsesData() {
Map<Class<?>, MultipartResponseData> result = JaxrsClientReactiveRecorder.getMultipartResponsesData();
return result == null
? DefaultClientContext.INSTANCE.getMultipartResponsesData()
: result;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import org.jboss.resteasy.reactive.client.impl.ClientProxies;
import org.jboss.resteasy.reactive.client.impl.ClientSerialisers;
import org.jboss.resteasy.reactive.client.spi.MultipartResponseData;
import org.jboss.resteasy.reactive.common.core.GenericTypeMapping;
import org.jboss.resteasy.reactive.common.core.Serialisers;

Expand All @@ -24,6 +25,7 @@ public class JaxrsClientReactiveRecorder extends ResteasyReactiveCommonRecorder

private static volatile Serialisers serialisers;
private static volatile GenericTypeMapping genericTypeMapping;
private static volatile Map<Class<?>, MultipartResponseData> multipartResponsesData;

private static volatile ClientProxies clientProxies = new ClientProxies(Collections.emptyMap(), Collections.emptyMap());

Expand All @@ -39,6 +41,19 @@ public static GenericTypeMapping getGenericTypeMapping() {
return genericTypeMapping;
}

public static Map<Class<?>, MultipartResponseData> getMultipartResponsesData() {
return multipartResponsesData;
}

public void setMultipartResponsesData(Map<String, RuntimeValue<MultipartResponseData>> multipartResponsesData) {
Map<Class<?>, MultipartResponseData> runtimeMap = new HashMap<>();
for (Map.Entry<String, RuntimeValue<MultipartResponseData>> multipartData : multipartResponsesData.entrySet()) {
runtimeMap.put(loadClass(multipartData.getKey()), multipartData.getValue().getValue());
}

JaxrsClientReactiveRecorder.multipartResponsesData = runtimeMap;
}

public void setupClientProxies(
Map<String, RuntimeValue<BiFunction<WebTarget, List<ParamConverterProvider>, ?>>> clientImplementations,
Map<String, String> failures) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.quarkus.jaxrs.client.reactive.runtime.impl;

import java.util.ArrayList;
import java.util.List;

import org.jboss.resteasy.reactive.client.spi.FieldFiller;
import org.jboss.resteasy.reactive.client.spi.MultipartResponseData;

public abstract class MultipartResponseDataBase implements MultipartResponseData {

private final List<FieldFiller> fillers = new ArrayList<>();

@Override
public List<FieldFiller> getFieldFillers() {
return fillers;
}

@SuppressWarnings("unused") // used in generated classes
public void addFiller(FieldFiller filler) {
fillers.add(filler);
}
}
Loading

0 comments on commit 3453f79

Please sign in to comment.