Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multipart download in REST Client Reactive #22855

Merged
merged 1 commit into from
Jan 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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