使用带有 Multipart 的旧版 REST 客户端
本指南介绍了与RESTEasy Classic兼容的REST客户端的Multipart支持,该客户端曾是Quarkus 2.8之前的默认Jakarta REST(以前称为JAX-RS)实现。 现在建议使用Quarkus REST(以前称为RESTEasy Reactive),它同样很好地支持传统的阻塞式工作负载和响应式工作负载。有关Quarkus REST的更多信息,请参阅REST客户端指南,有关服务器端,请参阅REST JSON入门指南或更详细的Quarkus REST指南。 |
RESTEasy对multipart/*
和multipart/form-data
MIME类型具有丰富的支持。Multipart MIME格式用于传递内容体列表。多个内容体嵌入在一个消息中。multipart/form-data
经常出现在Web应用程序HTML表单文档中,通常用于上传文件。form-data格式与其他multipart格式相同,不同之处在于每个内嵌的内容部分都与其关联了一个名称。
本指南将介绍如何使用RESTEasy REST客户端与Multipart配合,以非常轻松地与需要multipart/form-data
内容类型的REST API进行交互。
先决条件
要完成本指南,您需要
-
大约 15 分钟
-
一个 IDE
-
已安装 JDK 17+ 并正确配置了
JAVA_HOME
-
Apache Maven 3.9.9
-
如果您想使用它,可以选择 Quarkus CLI
-
如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置
解决方案
我们建议您按照以下章节中的说明,逐步创建应用程序。但是,您可以直接转到完整的示例。
克隆Git仓库:git clone https://github.com/quarkusio/quarkus-quickstarts.git
,或下载一个存档。
解决方案位于resteasy-client-multipart-quickstart
目录中。
创建 Maven 项目
首先,我们需要一个新项目。使用以下命令创建一个新项目
对于 Windows 用户
-
如果使用 cmd,(不要使用反斜杠
\
并将所有内容放在同一行上) -
如果使用Powershell,请将
-D
参数括在双引号中,例如"-DprojectArtifactId=resteasy-client-multipart-quickstart"
此命令会生成一个包含REST端点的Maven项目,并导入resteasy-client
和resteasy
扩展。它还添加了resteasy-multipart
扩展以支持multipart/form-data
请求。
如果您已经配置了Quarkus项目,可以通过在项目根目录下运行以下命令将resteasy-multipart
扩展添加到您的项目中
quarkus extension add resteasy-multipart
./mvnw quarkus:add-extension -Dextensions='resteasy-multipart'
./gradlew addExtension --extensions='resteasy-multipart'
这会将以下内容添加到您的构建文件中
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-multipart</artifactId>
</dependency>
implementation("io.quarkus:quarkus-resteasy-multipart")
设置模型
在本指南中,我们将演示如何调用接受multipart/form-data
输入的REST服务。我们假设在发送请求之前载荷是已知的,因此我们可以将其建模为POJO。
如果载荷未知,您也可以使用RESTEasy自定义API。在这种情况下,请参阅指南末尾的RESTEasy Multipart Providers链接。 |
我们的首要任务是设置我们将用于定义MultipartBody
POJO的multipart/form-data
载荷的模型。
创建一个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请求时需要,并定义部分的content type。
创建接口
使用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
方法使我们的代码能够将multipart/form-data
请求POST到我们的Echo服务(为演示目的,该服务运行在同一服务器上)。由于在本演示中我们确切地知道multipart/form-data
数据包,我们可以使用@org.jboss.resteasy.annotations.providers.multipart.MultipartForm
注解将其映射到上一节创建的模型类。
客户端将处理所有网络和编组,使我们的代码免于这些技术细节。
上面代码中注解的目的是以下几点
-
@RegisterRestClient
允许 Quarkus 知道此接口旨在作为 REST 客户端用于 CDI 注入 -
@Path
和@POST
是用于定义如何访问服务的标准Jakarta REST注解 -
@MultipartForm
将参数定义为multipart/form-data MIME类型的入站/出站请求/响应的值对象。 -
@Consumes
定义此请求(参数)消耗的预期content-type -
@Produces
定义此请求(返回类型)产生的预期content-type
虽然 这将允许缩小包含在原生可执行文件中的Jakarta REST提供商(可视为转换器)的数量。 |
创建配置
为了确定将进行 REST 调用的基本 URL,REST 客户端使用 application.properties
中的配置。属性的名称需要遵循一定的约定,最好在以下代码中显示
# Your configuration properties
quarkus.rest-client."org.acme.rest.client.multipart.MultipartService".url=https://:8080/
此配置意味着使用org.acme.rest.client.multipart.MultipartService
执行的所有请求都将使用https://:8080/
作为基础URL。
请注意,org.acme.rest.client.multipart.MultipartService
必须与我们在上一节中创建的MultipartService
接口的完全限定名称匹配。
创建 Jakarta REST 资源
创建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
。
创建服务器
出于演示目的,让我们创建一个简单的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;
}
}
这只会返回请求体,对于测试很有用。
更新测试
我们还需要更新功能测试以反映对端点所做的更改。编辑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服务返回的内容包含Multipart元素
由于测试运行在不同的端口,我们还需要在src/test/resources
中包含一个application.properties
文件,其内容如下
# Your configuration properties
quarkus.rest-client."org.acme.rest.client.multipart.MultipartService".url=https://:8081/
打包并运行应用程序
使用以下命令运行应用程序
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
在终端中,运行curl -X POST https://: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