使用 Jersey Client 发送 multipart/mixed 请求
Sending multipart/mixed request with Jersey Client
我想使用 Jersey (https://eclipse-ee4j.github.io/jersey/)
以编程方式与此 API 服务对话
这是 Spring 中的 Rest Controller 实现:
@PostMapping(
value = "/api/my-endpoint",
consumes = MediaType.MULTIPART_MIXED_VALUE)
public void enrichInvoice(@RequestPart("metadata") Map<String, Object> request,
@RequestPart("human") MultipartFile humanFile) {
log.info(String.format("received request:%n%s", request));
}
我的客户端实现是这样的
...
final Client client = ClientBuilder.newClient(new ClientConfig()
.register(MultiPartFeature.class)
.register(JacksonFeature.class)
);
final FileDataBodyPart filePart = new FileDataBodyPart("human",myFile()));
final BodyPart metadata = new BodyPart().entity(voBuilder.generateMetadata());
final MultiPart multiPartEntity = new MultiPart();
multiPartEntity.bodyPart(metadata, MediaType.APPLICATION_JSON_TYPE);
multiPartEntity.bodyPart(filePart);
final WebTarget target = client
.target("http://localhost:8080/api/my-endpoint");
final Entity<MultiPart> entity = Entity
.entity(multiPartEntity, multiPartEntity.getMediaType());
log.info(entity.toString());
final Response response = target
.request()
.post(entity);
log.info(String.format("%s", response.readEntity(String.class)));
response.close();
...
但是我一直收到这个错误:
Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'metadata' is not present]
这是因为元数据部分必须命名为“元数据”。而且我找不到使用 BodyPart 命名它的方法。我还尝试使用 FormDataBodyPart 构建元数据
FormDataBodyPart metadataBodyPart = new FormDataBodyPart("metadata", metadata,
MediaType.APPLICATION_JSON_TYPE);
但结果相同。
你能帮我弄清楚 bodyPart 定义中缺少什么吗?
谢谢
编辑:这是从我的客户端实现发送的 http 请求
Content-Type: multipart/mixed;boundary=Boundary_1_1972899462_1597045386454
User-Agent: Jersey/2.29 (HttpUrlConnection 11.0.8)
MIME-Version: 1.0
Host: localhost:8080
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 765
--Boundary_1_1972899462_1597045386454
Content-Type: application/json
{"value":"key"}
--Boundary_1_1972899462_1597045386454
Content-Type: application/octet-stream
Content-Disposition: form-data; filename="file.zip"; modification-date="Wed, 05 Aug 2020 16:52:52 GMT"; size=0; name="human"
--Boundary_1_1972899462_1597045386454--
]
即使不是最优的解决方案,也是将元数据视为文本文件
final Path tempFile = Files.createTempFile("prefix", "suffix");
File fileMetadata = Files.write(tempFile.toAbsolutePath(), JsonUtils.toString(metadata).getBytes());
final FileDataBodyPart metadataBodyPart = new FileDataBodyPart(
"metadata",
fileMetadata,
MediaType.APPLICATION_JSON_TYPE);
final FileDataBodyPart human = new FileDataBodyPart("human", new File(humanReadableFile.getFileKey()));
try (final MultiPart multiPartEntity = new MultiPart()) {
multiPartEntity.bodyPart(metadataBodyPart);
multiPartEntity.bodyPart(human);
final Response response = client
.target("http://localhost:8080/api/my-endpoint")
.request()
.post(Entity.entity(multiPartEntity, multiPartEntity.getMediaType()));
log.debug(String.valueOf(response.getStatus()));
log.debug(response.readEntity(String.class));
}
通过这种方式,请求主体必须按照控制器实现的要求命名为“元数据”和“人”的部分,并且仍然保持 multipart/mixed content-type.
我想使用 Jersey (https://eclipse-ee4j.github.io/jersey/)
以编程方式与此 API 服务对话这是 Spring 中的 Rest Controller 实现:
@PostMapping(
value = "/api/my-endpoint",
consumes = MediaType.MULTIPART_MIXED_VALUE)
public void enrichInvoice(@RequestPart("metadata") Map<String, Object> request,
@RequestPart("human") MultipartFile humanFile) {
log.info(String.format("received request:%n%s", request));
}
我的客户端实现是这样的
...
final Client client = ClientBuilder.newClient(new ClientConfig()
.register(MultiPartFeature.class)
.register(JacksonFeature.class)
);
final FileDataBodyPart filePart = new FileDataBodyPart("human",myFile()));
final BodyPart metadata = new BodyPart().entity(voBuilder.generateMetadata());
final MultiPart multiPartEntity = new MultiPart();
multiPartEntity.bodyPart(metadata, MediaType.APPLICATION_JSON_TYPE);
multiPartEntity.bodyPart(filePart);
final WebTarget target = client
.target("http://localhost:8080/api/my-endpoint");
final Entity<MultiPart> entity = Entity
.entity(multiPartEntity, multiPartEntity.getMediaType());
log.info(entity.toString());
final Response response = target
.request()
.post(entity);
log.info(String.format("%s", response.readEntity(String.class)));
response.close();
...
但是我一直收到这个错误:
Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'metadata' is not present]
这是因为元数据部分必须命名为“元数据”。而且我找不到使用 BodyPart 命名它的方法。我还尝试使用 FormDataBodyPart 构建元数据
FormDataBodyPart metadataBodyPart = new FormDataBodyPart("metadata", metadata,
MediaType.APPLICATION_JSON_TYPE);
但结果相同。
你能帮我弄清楚 bodyPart 定义中缺少什么吗?
谢谢
编辑:这是从我的客户端实现发送的 http 请求
Content-Type: multipart/mixed;boundary=Boundary_1_1972899462_1597045386454
User-Agent: Jersey/2.29 (HttpUrlConnection 11.0.8)
MIME-Version: 1.0
Host: localhost:8080
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 765
--Boundary_1_1972899462_1597045386454
Content-Type: application/json
{"value":"key"}
--Boundary_1_1972899462_1597045386454
Content-Type: application/octet-stream
Content-Disposition: form-data; filename="file.zip"; modification-date="Wed, 05 Aug 2020 16:52:52 GMT"; size=0; name="human"
--Boundary_1_1972899462_1597045386454--
]
即使不是最优的解决方案,也是将元数据视为文本文件
final Path tempFile = Files.createTempFile("prefix", "suffix");
File fileMetadata = Files.write(tempFile.toAbsolutePath(), JsonUtils.toString(metadata).getBytes());
final FileDataBodyPart metadataBodyPart = new FileDataBodyPart(
"metadata",
fileMetadata,
MediaType.APPLICATION_JSON_TYPE);
final FileDataBodyPart human = new FileDataBodyPart("human", new File(humanReadableFile.getFileKey()));
try (final MultiPart multiPartEntity = new MultiPart()) {
multiPartEntity.bodyPart(metadataBodyPart);
multiPartEntity.bodyPart(human);
final Response response = client
.target("http://localhost:8080/api/my-endpoint")
.request()
.post(Entity.entity(multiPartEntity, multiPartEntity.getMediaType()));
log.debug(String.valueOf(response.getStatus()));
log.debug(response.readEntity(String.class));
}
通过这种方式,请求主体必须按照控制器实现的要求命名为“元数据”和“人”的部分,并且仍然保持 multipart/mixed content-type.