Using the legacy REST Client with Multipart
This guide is about the multipart support of the REST Client compatible with RESTEasy Classic which used to be the default Jakarta REST (formerly known as JAX-RS) implementation until Quarkus 2.8. It is now recommended to use Quarkus REST (formerly RESTEasy Reactive), which supports equally well traditional blocking workloads and reactive workloads. For more information about Quarkus REST, please see the REST Client guide and, for the server side, the introductory REST JSON guide or the more detailed Quarkus REST guide. |
RESTEasy has rich support for the multipart/*
and multipart/form-data
mime types. The multipart mime format is used to pass lists of content bodies. Multiple content bodies are embedded in one message. multipart/form-data
is often found in web application HTML Form documents and is generally used to upload files. The form-data format is the same as other multipart formats, except that each inlined piece of content has a name associated with it.
This guide explains how to use the RESTEasy REST Client with Multipart in order to interact with REST APIs
requiring multipart/form-data
content-type with very little effort.
Pré-requisitos
Para concluir este guia, você precisa:
-
Cerca de 15 minutos
-
Um IDE
-
JDK 17+ installed with
JAVA_HOME
configured appropriately -
Apache Maven 3.9.9
-
Opcionalmente, o Quarkus CLI se você quiser usá-lo
-
Opcionalmente, Mandrel ou GraalVM instalado e configurado apropriadamente se você quiser criar um executável nativo (ou Docker se você usar uma compilação de contêiner nativo)
Solução
Recomendamos que siga as instruções nas seções seguintes e crie a aplicação passo a passo. No entanto, você pode ir diretamente para o exemplo completo.
Clone o repositório Git: git clone https://github.com/quarkusio/quarkus-quickstarts.git
, ou baixe um arquivo.
The solution is located in the resteasy-client-multipart-quickstart
directory.
Criar o projeto Maven
Primeiro, precisamos de um novo projeto. Crie um novo projeto com o seguinte comando:
Para usuários do Windows:
-
Se estiver usando cmd, (não use barra invertida '\' e coloque tudo na mesma linha)
-
Se estiver usando o Powershell, envolva os parâmetros '-D' entre aspas duplas, por exemplo, '"-DprojectArtifactId=resteasy-client-multipart-quickstart"'
This command generates a Maven project with a REST endpoint and imports the resteasy-client
and resteasy
extensions.
It also adds the resteasy-multipart
extension to support multipart/form-data
requests.
If you already have your Quarkus project configured, you can add the resteasy-multipart
extension
to your project by running the following command in your project base directory:
quarkus extension add resteasy-multipart
./mvnw quarkus:add-extension -Dextensions='resteasy-multipart'
./gradlew addExtension --extensions='resteasy-multipart'
Isto irá adicionar o seguinte trecho no seu arquivo de build:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-multipart</artifactId>
</dependency>
implementation("io.quarkus:quarkus-resteasy-multipart")
Configurando o modelo
In this guide we will be demonstrating how to invoke a REST service accepting multipart/form-data
input.
We are assuming the payload is well-known before the request is sent, so we can model as a POJO.
If the payload is unknown, you can also use the RESTEasy custom API instead. If that’s the case, see the RESTEasy Multipart Providers link at the end of the guide. |
Our first order of business is to set up the model we will be using to define the multipart/form-data
payload, in the form of a MultipartBody
POJO.
Create a src/main/java/org/acme/rest/client/multipart/MultipartBody.java
file and set the following content:
package org.acme.rest.client.multipart;
import java.io.InputStream;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.providers.multipart.PartType;
public class MultipartBody {
@FormParam("file")
@PartType(MediaType.APPLICATION_OCTET_STREAM)
public InputStream file;
@FormParam("fileName")
@PartType(MediaType.TEXT_PLAIN)
public String fileName;
}
O objetivo das anotações no código acima é o seguinte:
-
@FormParam
is a standard Jakarta REST annotation used to define a form parameter contained within a request entity body -
@PartType
is a RESTEasy specific annotation required when a client performs a multipart request and defines the content type for the part.
Crie a interface
Using the RESTEasy REST Client is as simple as creating an interface using the proper Jakarta REST and MicroProfile annotations. In our case the interface should be created at src/main/java/org/acme/rest/client/multipart/MultipartService.java
and have the following content:
package org.acme.rest.client.multipart;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
@Path("/echo")
@RegisterRestClient
public interface MultipartService {
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
String sendMultipartData(@MultipartForm MultipartBody data);
}
The sendMultipartData
method gives our code the ability to POST a multipart/form-data
request to our Echo service (running in the same server for demo purposes).
Because in this demo we have the exact knowledge of the multipart/form-data
packets, we can map them to the model class created in the previous section using the @org.jboss.resteasy.annotations.providers.multipart.MultipartForm
annotation.
The client will handle all the networking and marshalling leaving our code clean of such technical details.
O objetivo das anotações no código acima é o seguinte:
-
@RegisterRestClient
permite que o Quarkus saiba que essa interface deve estar disponível para injeção de CDI como um Cliente REST -
@Path
and@POST
are the standard Jakarta REST annotations used to define how to access the service -
@MultipartForm
defines the parameter as a value object for incoming/outgoing request/responses of the multipart/form-data mime type. -
@Consumes
defines the expected content-type consumed by this request (parameters) -
@Produces
defines the expected content-type produced by this request (return type)
While It will allow to narrow down the number of Jakarta REST providers (which can be seen as converters) included in the native executable. |
Crie a configuração
Para determinar o URL de base para o qual as chamadas REST serão feitas, o Cliente REST usa a configuração de application.properties
. O nome da propriedade precisa seguir uma determinada convenção que é melhor exibida no código a seguir:
# Your configuration properties
quarkus.rest-client."org.acme.rest.client.multipart.MultipartService".url=http://localhost:8080/
Having this configuration means that all requests performed using org.acme.rest.client.multipart.MultipartService
will use http://localhost:8080/
as the base URL.
Note that org.acme.rest.client.multipart.MultipartService
must match the fully qualified name of the MultipartService
interface we created in the previous section.
Crie o recurso Jakarta REST
Create the src/main/java/org/acme/rest/client/multipart/MultipartClientResource.java
file with the following content:
package org.acme.rest.client.multipart;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import jakarta.inject.Inject;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.rest.client.inject.RestClient;
@Path("/client")
public class MultipartClientResource {
@Inject
@RestClient
MultipartService service;
@POST
@Path("/multipart")
@Produces(MediaType.TEXT_PLAIN)
public String sendFile() throws Exception {
MultipartBody body = new MultipartBody();
body.fileName = "greeting.txt";
body.file = new ByteArrayInputStream("HELLO WORLD".getBytes(StandardCharsets.UTF_8));
return service.sendMultipartData(body);
}
}
Note that in addition to the standard CDI @Inject
annotation, we also need to use the MicroProfile @RestClient
annotation to inject MultipartService
.
Creating the server
For demo purposes, let’s create a simple Echo endpoint that will act as the server part.
Create the directory src/main/java/org/acme/rest/client/multipart/server
and include a EchoService.java
file with the following content:
package org.acme.rest.client.multipart.server;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/echo")
public class EchoService {
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
public String echo(String requestBody) throws Exception {
return requestBody;
}
}
This will just return the request body and it’s useful for testing purposes.
Atualize o teste
We also need to update the functional test to reflect the changes made to the endpoint.
Edit the src/test/java/org/acme/rest/client/multipart/MultipartClientResourceTest.java
file to:
package org.acme.rest.client.multipart;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.containsString;
@QuarkusTest
public class MultipartClientResourceTest {
@Test
public void testMultipartDataIsSent() {
given()
.when().post("/client/multipart")
.then()
.statusCode(200)
.body( containsString("Content-Disposition: form-data; name=\"file\""),
containsString("HELLO WORLD"),
containsString("Content-Disposition: form-data; name=\"fileName\""),
containsString("greeting.txt"));
}
}
The code above uses REST Assured to assert that the returned content from the echo service contains multipart elements
Because the test runs in a different port, we also need to include an application.properties
in our src/test/resources
with the following content:
# Your configuration properties
quarkus.rest-client."org.acme.rest.client.multipart.MultipartService".url=http://localhost:8081/
Empacote e execute a aplicação
Execute a aplicação com:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
In a terminal, run curl -X POST http://localhost:8080/client/multipart
You should see an output similar to:
--89d288bd-960f-460c-b266-64c5b4d170fa
Content-Disposition: form-data; name="fileName"
Content-Type: text/plain
greeting.txt
--89d288bd-960f-460c-b266-64c5b4d170fa
Content-Disposition: form-data; name="file"
Content-Type: application/octet-stream
HELLO WORLD
--89d288bd-960f-460c-b266-64c5b4d170fa--
Como de costume, a aplicação pode ser empacotada utilizando:
quarkus build
./mvnw install
./gradlew build
E executado com java -jar target/quarkus-app/quarkus-run.jar
.
Também é possível gerar o executável nativo com:
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true