Using the legacy REST Client with Multipart
本指南介绍了与 RESTEasy Classic 兼容的 REST 客户端的多部分支持,该指南曾经是默认的 Jakarta REST(以前称为 JAX-RS)实现,直到 Quarkus 2.8。 现在建议使用 Quarkus REST(以前称为 RESTEasy Reactive),它同样支持传统的阻塞工作负载和反应式工作负载。有关 Quarkus REST 的更多信息,请参阅 REST Client guide,对于服务器端,请参阅 introductory REST JSON guide 或更详细的 Quarkus REST guide。
RESTEasy 丰富地支持 multipart/*
和 multipart/form-data
mime 类型。多部分 mime 格式用于传递内容正文列表。多个内容正文嵌入在一则消息中。multipart/form-data
通常在 Web 应用程序 HTML 表单文档中找到,通常用于上传文件。表单数据格式与其他多部分格式相同,只是每段内联内容都有与之关联的名称。
本指南解释了如何使用 RESTEasy REST 客户端与 Multipart 协同使用,以便与几乎不需要任何内容的 multipart/form-data
内容类型所需的 REST API 进行交互。
Prerequisites
如要完成本指南,您需要:
-
Roughly 15 minutes
-
An IDE
-
安装了 JDK 17+,已正确配置
JAVA_HOME
-
Apache Maven ${proposed-maven-version}
-
如果你想使用 Quarkus CLI, 则可以选择使用
-
如果你想构建一个本机可执行文件(或如果你使用本机容器构建,则使用 Docker),则可以选择安装 Mandrel 或 GraalVM 以及 configured appropriately
Solution
我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。
克隆 Git 存储库: git clone $${quickstarts-base-url}.git
,或下载 $${quickstarts-base-url}/archive/main.zip[存档]。
解决方案位于 resteasy-client-multipart-quickstart
directory。
Creating the Maven project
首先,我们需要一个新项目。使用以下命令创建一个新项目:
quarkus create app {create-app-group-id}:{create-app-artifact-id} \
--no-code
cd {create-app-artifact-id}
要创建一个 Gradle 项目,添加 --gradle
或 --gradle-kotlin-dsl
选项。
有关如何安装和使用 Quarkus CLI 的详细信息,请参见 Quarkus CLI 指南。
mvn {quarkus-platform-groupid}:quarkus-maven-plugin:{quarkus-version}:create \
-DprojectGroupId={create-app-group-id} \
-DprojectArtifactId={create-app-artifact-id} \
-DnoCode
cd {create-app-artifact-id}
要创建一个 Gradle 项目,添加 -DbuildTool=gradle
或 -DbuildTool=gradle-kotlin-dsl
选项。
适用于 Windows 用户:
-
如果使用 cmd,(不要使用反斜杠
\
,并将所有内容放在同一行上) -
如果使用 Powershell,将
-D
参数用双引号引起来,例如"-DprojectArtifactId={create-app-artifact-id}"
此命令生成一个具有 REST 端点的 Maven 项目,并导入 resteasy-client
和 resteasy
扩展。它还添加了 resteasy-multipart
扩展来支持 multipart/form-data
请求。
如果你已经配置了 Quarkus 项目,则可以通过在项目基目录中运行以下命令将 resteasy-multipart
扩展添加到项目:
quarkus extension add {add-extension-extensions}
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
./gradlew addExtension --extensions='{add-extension-extensions}'
这会将以下内容添加到构建文件中:
<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。
如果有效负载未知,您还可以使用 RESTEasy 自定义 API。如果是这种情况,请参阅本指南末尾处的 RESTEasy Multipart Providers 链接。 |
我们的首要任务是设置我们将用来定义 multipart/form-data
有效负载的模型,形式为 MultipartBody
POJO。
创建 src/main/java/org/acme/rest/client/multipart/MultipartBody.java
文件并设置以下内容:
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;
}
上述代码中注释的目的如下:
-
@FormParam
是一个标准 Jakarta REST 注解,用于定义请求实体主体中包含的表单参数 -
@PartType
是 RESTEasy 特定的注解,当客户端执行 multipart 请求并定义该部分的内容类型时需要。
Create the interface
使用 RESTEasy REST 客户端很简单,只需使用适当的 Jakarta REST 和 MicroProfile 注解创建一个接口即可。在我们的示例中,应该在 src/main/java/org/acme/rest/client/multipart/MultipartService.java
创建接口并具有以下内容:
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
注解将它们映射到上一节中创建的模型类。
客户端将处理所有网络和编组,从而使我们的代码摆脱此类技术细节。
上述代码中注释的目的如下:
-
@RegisterRestClient
允许 Quarkus 知道此接口旨在作为 REST 客户端用于 CDI 注入 -
@Path
和@POST
是标准 Jakarta REST 注解,用于定义如何访问服务 -
@MultipartForm
定义参数作为 multipart/form-data MIME 类型的传入/传出请求/响应的值对象。 -
@Consumes
定义此请求消耗的预期内容类型(参数) -
@Produces
定义此请求产生的预期内容类型(返回类型)
虽然 |
Create the configuration
要确定发起 REST 调用时的基本 URL,REST 客户端使用 application.properties
的配置。属性的名称需要遵循特定的约定,这在下面的代码中展示得最好:
# 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。
请注意,org.acme.rest.client.multipart.MultipartService
must 匹配我们在上一节中创建的 MultipartService
接口的完全限定名称。
Create the Jakarta REST resource
使用以下内容创建 src/main/java/org/acme/rest/client/multipart/MultipartClientResource.java
文件:
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
。
Creating the server
为了演示目的,我们创建一个简单的 Echo 终端,作为服务器部分。
创建目录 src/main/java/org/acme/rest/client/multipart/server
并包含一个 EchoService.java
文件,内含以下内容:
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;
}
}
这将仅返回请求正文,这对于测试目的很有用。
Update the test
我们还需要更新功能测试以反映对终端做的更改。编辑 src/test/java/org/acme/rest/client/multipart/MultipartClientResourceTest.java
文件为:
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 服务返回的内容包含多部分元素
因为测试在不同的端口中运行,所以我们还需要在 src/test/resources
中包含一个 application.properties
,内含以下内容:
# Your configuration properties
quarkus.rest-client."org.acme.rest.client.multipart.MultipartService".url=http://localhost:8081/
Package and run the application
使用以下内容运行应用程序:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
在终端中,运行 curl -X POST [role="bare"]http://localhost:8080/client/multipart
你会看到类似以下的输出:
--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--
和往常一样,可以使用以下命令打包应用程序:
quarkus build
./mvnw install
./gradlew build
并且使用 java -jar target/quarkus-app/quarkus-run.jar
执行。
你还可以按如下方式生成本机可执行文件:
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true