Using the legacy REST Client with Multipart
本指南介绍了与 RESTEasy Classic 兼容的 REST 客户端的多部分支持,该指南曾经是默认的 Jakarta REST(以前称为 JAX-RS)实现,直到 Quarkus 2.8。
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.
现在建议使用 Quarkus REST(以前称为 RESTEasy Reactive),它同样支持传统的阻塞工作负载和反应式工作负载。有关 Quarkus REST 的更多信息,请参阅 REST Client guide,对于服务器端,请参阅 introductory REST JSON guide 或更详细的 Quarkus REST guide。
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 丰富地支持 multipart/*
和 multipart/form-data
mime 类型。多部分 mime 格式用于传递内容正文列表。多个内容正文嵌入在一则消息中。multipart/form-data
通常在 Web 应用程序 HTML 表单文档中找到,通常用于上传文件。表单数据格式与其他多部分格式相同,只是每段内联内容都有与之关联的名称。
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.
本指南解释了如何使用 RESTEasy REST 客户端与 Multipart 协同使用,以便与几乎不需要任何内容的 multipart/form-data
内容类型所需的 REST API 进行交互。
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.
Prerequisites
Unresolved directive in resteasy-client-multipart.adoc - include::{includes}/prerequisites.adoc[]
Solution
我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。
We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.
克隆 Git 存储库: git clone {quickstarts-clone-url}
,或下载 {quickstarts-archive-url}[存档]。
Clone the Git repository: git clone {quickstarts-clone-url}
, or download an {quickstarts-archive-url}[archive].
解决方案位于 resteasy-client-multipart-quickstart
directory。
The solution is located in the resteasy-client-multipart-quickstart
directory.
Creating the Maven project
首先,我们需要一个新项目。使用以下命令创建一个新项目:
First, we need a new project. Create a new project with the following command:
Unresolved directive in resteasy-client-multipart.adoc - include::{includes}/devtools/create-app.adoc[]
此命令生成一个具有 REST 端点的 Maven 项目,并导入 resteasy-client
和 resteasy
扩展。它还添加了 resteasy-multipart
扩展来支持 multipart/form-data
请求。
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.
如果你已经配置了 Quarkus 项目,则可以通过在项目基目录中运行以下命令将 resteasy-multipart
扩展添加到项目:
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:
Unresolved directive in resteasy-client-multipart.adoc - include::{includes}/devtools/extension-add.adoc[]
这会将以下内容添加到构建文件中:
This will add the following to your build file:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-multipart</artifactId>
</dependency>
implementation("io.quarkus:quarkus-resteasy-multipart")
Setting up the model
在本指南中,我们将演示如何调用接受 multipart/form-data
输入的 REST 服务。我们假设在发送请求之前有效负载是众所周知的,因此我们可以建模为 POJO。
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.
如果有效负载未知,您还可以使用 RESTEasy 自定义 API。如果是这种情况,请参阅本指南末尾处的 RESTEasy Multipart Providers 链接。 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. |
我们的首要任务是设置我们将用来定义 multipart/form-data
有效负载的模型,形式为 MultipartBody
POJO。
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.
创建 src/main/java/org/acme/rest/client/multipart/MultipartBody.java
文件并设置以下内容:
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;
}
上述代码中注释的目的如下:
The purpose of the annotations in the code above is the following:
-
@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.
Create the interface
使用 RESTEasy REST 客户端很简单,只需使用适当的 Jakarta REST 和 MicroProfile 注解创建一个接口即可。在我们的示例中,应该在 src/main/java/org/acme/rest/client/multipart/MultipartService.java
创建接口并具有以下内容:
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);
}
sendMultipartData
方法使我们的代码能够向 Echo 服务(出于演示目的在同一服务器中运行)POST 一个 multipart/form-data
请求。由于在这个演示中我们对 multipart/form-data
数据包有确切的了解,因此我们可以使用 @org.jboss.resteasy.annotations.providers.multipart.MultipartForm
注解将它们映射到上一节中创建的模型类。
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.
上述代码中注释的目的如下:
The purpose of the annotations in the code above is the following:
-
@RegisterRestClient
allows Quarkus to know that this interface is meant to be available for CDI injection as a REST Client -
@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 它将允许缩小包含在原生可执行文件中的 Jakarta REST 提供程序(可以看作转换器)的数量。 It will allow to narrow down the number of Jakarta REST providers (which can be seen as converters) included in the native executable. |
Create the configuration
要确定发起 REST 调用时的基本 URL,REST 客户端使用 application.properties
的配置。属性的名称需要遵循特定的约定,这在下面的代码中展示得最好:
In order to determine the base URL to which REST calls will be made, the REST Client uses configuration from application.properties
.
The name of the property needs to follow a certain convention which is best displayed in the following code:
# Your configuration properties
quarkus.rest-client."org.acme.rest.client.multipart.MultipartService".url=http://localhost:8080/
此配置表示使用 org.acme.rest.client.multipart.MultipartService
执行的所有请求都将使用 http://localhost:8080/
作为基本 URL。
Having this configuration means that all requests performed using org.acme.rest.client.multipart.MultipartService
will use http://localhost:8080/
as the base URL.
请注意,org.acme.rest.client.multipart.MultipartService
must 匹配我们在上一节中创建的 MultipartService
接口的完全限定名称。
Note that org.acme.rest.client.multipart.MultipartService
must match the fully qualified name of the MultipartService
interface we created in the previous section.
Create the Jakarta REST resource
使用以下内容创建 src/main/java/org/acme/rest/client/multipart/MultipartClientResource.java
文件:
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);
}
}
请注意,除了标准 CDI @Inject
注解之外,我们还需要使用 MicroProfile @RestClient
注解来注入 MultipartService
。
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
为了演示目的,我们创建一个简单的 Echo 终端,作为服务器部分。
For demo purposes, let’s create a simple Echo endpoint that will act as the server part.
创建目录 src/main/java/org/acme/rest/client/multipart/server
并包含一个 EchoService.java
文件,内含以下内容:
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.
Update the test
我们还需要更新功能测试以反映对终端做的更改。编辑 src/test/java/org/acme/rest/client/multipart/MultipartClientResourceTest.java
文件为:
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"));
}
}
上面的代码使用 REST Assured 来断言从 echo 服务返回的内容包含多部分元素
The code above uses REST Assured to assert that the returned content from the echo service contains multipart elements
因为测试在不同的端口中运行,所以我们还需要在 src/test/resources
中包含一个 application.properties
,内含以下内容:
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/
Package and run the application
使用以下内容运行应用程序:
Run the application with:
Unresolved directive in resteasy-client-multipart.adoc - include::{includes}/devtools/dev.adoc[]
在终端中,运行 curl -X POST [role="bare"]http://localhost:8080/client/multipart
In a terminal, run curl -X POST [role="bare"]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--
和往常一样,可以使用以下命令打包应用程序:
As usual, the application can be packaged using:
Unresolved directive in resteasy-client-multipart.adoc - include::{includes}/devtools/build.adoc[]
并且使用 java -jar target/quarkus-app/quarkus-run.jar
执行。
And executed with java -jar target/quarkus-app/quarkus-run.jar
.
你还可以按如下方式生成本机可执行文件:
You can also generate the native executable with:
Unresolved directive in resteasy-client-multipart.adoc - include::{includes}/devtools/build-native.adoc[]