如何在 Java 中以正确的方式将 List<> 项目作为 POST 数据发送?
How can I send List<> items as POST data the right way in Java?
我正在开发一项 java 服务,其中来自 Table A 的数据转到 Table B。该服务向 REST 服务器发送 POST 请求并为复制的每一行,都会创建一个位置 header 作为响应,确认已创建新资源并在向新创建的资源发出 GET 请求时将 POJO 作为 JSON 返回。这是处理 POST 请求的资源方法。
@POST
@Produces({MediaType.APPLICATION_JSON})
public Response migrateToMinio(@Context UriInfo uriInfo) throws Exception {
tiedostoService = new TiedostoService();
attachmentService = new AttachmentService();
List<Tiedosto> tiedostoList = tiedostoService.getAllFiles();
List<String> responseList = new ArrayList<>();
Response r;
Integer newRow;
String responseData = null;
int i=1;
for (Tiedosto tiedosto : tiedostoList) {
Attachment attachment = new Attachment();
attachment.setCustomerId(tiedosto.getCustomerId());
attachment.setSize(tiedosto.getFileSize());
newRow = attachmentService.createNew(attachment);
UriBuilder builder = uriInfo.getAbsolutePathBuilder();
if (newRow == 1) {
builder.path(Integer.toString(i));
r = Response.created(builder.build()).build();
responseData = r.getLocation().toString();
i++;
}
responseList.add(responseData);
}
String jsonString = new Gson().toJson(responseList);
return Response.status(Response.Status.OK).entity(jsonString).build();
}
tiedostoService
和 attachmentService
为两个 table 服务 类。 tiedostoList
包含 tiedosto
table 中的所有行,并在 for 循环内迭代,以便为 tiedostoList
中的每个项目创建附件 table 中的新行.当我向 /rest/attachments
发送 POST 请求时,需要几秒钟的时间来处理 returns 状态 200 以及创建的资源列表,如下所示:
现在,我的问题是,有没有一种方法可以实现它,以便在创建后立即返回新创建的资源位置(可能作为 201 创建),而不必等待最终状态 200?
这样的解决方案假定您保存附件 (AttachmentService
) 的服务能够在保存之前计算出位置。例如,服务应该能够知道,如果最后一个附件的 ID 为 10
,则下一个附件的 ID 为 11
(或者后续 ID 为计算),因此位置为 http://localhost:8080/rest/attachments/11
.
假设该逻辑在您的服务中是可行的,您可以创建一个收据对象,其中包含已创建资源的位置和代表已保存资源的未来。该收据对象可以由服务而不是附件本身返回。此类收据对象的实现可能类似于以下内容:
public class CreationReceipt<T> {
private final String location;
private final Future<T> attachment;
public CreationReceipt(String location, Future<T> attachment) {
this.location = location;
this.attachment = attachment;
}
public String getLocation() {
return location;
}
public Future<T> getAttachment() {
return attachment;
}
}
在AttachmentService
中,存在一个存储新Attachment
和returns一个CreationReceipt<Attachment>
:
的方法
public class AttachmentService {
public CreationReceipt<Attachment> createNew(Attachment attachment) {
String location = computeLocationFor(attachment);
Future<Attachment> savedAttachment = saveAsynchronously(attachment);
return new CreationReceipt<>(location, savedAttachment);
}
private Future<Attachment> saveAsynchronously(Attachment attachment) { ... }
private String computeLocationFor(Attachment attachment) { ... }
}
预先计算 Attachment
位置的逻辑将取决于您的应用程序的具体情况(我将保留它以供您添加逻辑),但是保存 Attachment
的逻辑=19=] 异步可以遵循一个通用的模式。使用 ExecutorService
,用于保存 Attachment
(您的应用程序已经使用)的同步逻辑可以异步进行。为此,同步逻辑被提交给 ExecutorService
(使用 submit
方法)并且 ExecutorService
返回一个 Future
,它包装保存的 Attachment
并在 Attachment
成功保存后完成。
一个示例(虽然不完整)实现类似于:
public class AttachmentService {
private final ExecutorService executor = Executors.newSingleThreadExecutor();
public CreationReceipt<Attachment> createNew(Attachment attachment) {
String location = computeLocationFor(attachment);
Future<Attachment> savedAttachment = saveAsynchronously(attachment);
return new CreationReceipt<>(location, savedAttachment);
}
private Future<Attachment> saveAsynchronously(Attachment attachment) {
return executor.submit(() -> save(attachment));
}
private Attachment save(Attachment attachment) { ... }
private String computeLocationFor(Attachment attachment) { ... }
}
请注意,此实现一次只允许保存一个 Attachment
(使用 Executors.newSingleThreadExecutor()
的性质),但可以通过定义 executor
来更改该逻辑以使用不同的 ExecutorService
实现。要完成此实现,只需为同步保存新 Attachment
对象的 save
方法添加一个实现(此同步逻辑应在您现有的 createNew
方法中找到 AttachmentService
).
从这一点来看,保存 Attachment
的逻辑将是:
List<String> savedLocations = new ArrayList<>();
for (Tiedosto tiedosto : tiedostoList) {
Attachment attachment = new Attachment();
attachment.setCustomerId(tiedosto.getCustomerId());
attachment.setSize(tiedosto.getFileSize());
CreationReceipt<Attachment> receipt = attachmentService.createNew(attachmentToSave);
savedLocations.add(receipt.getLocation());
}
// Do something with savedLocations
这是一个不完整的实现,但它应该演示将要使用的基本结构。循环完成后,您可以将 saveLocations
作为响应的主体并将响应状态代码设置为 201 Created
.
我正在开发一项 java 服务,其中来自 Table A 的数据转到 Table B。该服务向 REST 服务器发送 POST 请求并为复制的每一行,都会创建一个位置 header 作为响应,确认已创建新资源并在向新创建的资源发出 GET 请求时将 POJO 作为 JSON 返回。这是处理 POST 请求的资源方法。
@POST
@Produces({MediaType.APPLICATION_JSON})
public Response migrateToMinio(@Context UriInfo uriInfo) throws Exception {
tiedostoService = new TiedostoService();
attachmentService = new AttachmentService();
List<Tiedosto> tiedostoList = tiedostoService.getAllFiles();
List<String> responseList = new ArrayList<>();
Response r;
Integer newRow;
String responseData = null;
int i=1;
for (Tiedosto tiedosto : tiedostoList) {
Attachment attachment = new Attachment();
attachment.setCustomerId(tiedosto.getCustomerId());
attachment.setSize(tiedosto.getFileSize());
newRow = attachmentService.createNew(attachment);
UriBuilder builder = uriInfo.getAbsolutePathBuilder();
if (newRow == 1) {
builder.path(Integer.toString(i));
r = Response.created(builder.build()).build();
responseData = r.getLocation().toString();
i++;
}
responseList.add(responseData);
}
String jsonString = new Gson().toJson(responseList);
return Response.status(Response.Status.OK).entity(jsonString).build();
}
tiedostoService
和 attachmentService
为两个 table 服务 类。 tiedostoList
包含 tiedosto
table 中的所有行,并在 for 循环内迭代,以便为 tiedostoList
中的每个项目创建附件 table 中的新行.当我向 /rest/attachments
发送 POST 请求时,需要几秒钟的时间来处理 returns 状态 200 以及创建的资源列表,如下所示:
现在,我的问题是,有没有一种方法可以实现它,以便在创建后立即返回新创建的资源位置(可能作为 201 创建),而不必等待最终状态 200?
这样的解决方案假定您保存附件 (AttachmentService
) 的服务能够在保存之前计算出位置。例如,服务应该能够知道,如果最后一个附件的 ID 为 10
,则下一个附件的 ID 为 11
(或者后续 ID 为计算),因此位置为 http://localhost:8080/rest/attachments/11
.
假设该逻辑在您的服务中是可行的,您可以创建一个收据对象,其中包含已创建资源的位置和代表已保存资源的未来。该收据对象可以由服务而不是附件本身返回。此类收据对象的实现可能类似于以下内容:
public class CreationReceipt<T> {
private final String location;
private final Future<T> attachment;
public CreationReceipt(String location, Future<T> attachment) {
this.location = location;
this.attachment = attachment;
}
public String getLocation() {
return location;
}
public Future<T> getAttachment() {
return attachment;
}
}
在AttachmentService
中,存在一个存储新Attachment
和returns一个CreationReceipt<Attachment>
:
public class AttachmentService {
public CreationReceipt<Attachment> createNew(Attachment attachment) {
String location = computeLocationFor(attachment);
Future<Attachment> savedAttachment = saveAsynchronously(attachment);
return new CreationReceipt<>(location, savedAttachment);
}
private Future<Attachment> saveAsynchronously(Attachment attachment) { ... }
private String computeLocationFor(Attachment attachment) { ... }
}
预先计算 Attachment
位置的逻辑将取决于您的应用程序的具体情况(我将保留它以供您添加逻辑),但是保存 Attachment
的逻辑=19=] 异步可以遵循一个通用的模式。使用 ExecutorService
,用于保存 Attachment
(您的应用程序已经使用)的同步逻辑可以异步进行。为此,同步逻辑被提交给 ExecutorService
(使用 submit
方法)并且 ExecutorService
返回一个 Future
,它包装保存的 Attachment
并在 Attachment
成功保存后完成。
一个示例(虽然不完整)实现类似于:
public class AttachmentService {
private final ExecutorService executor = Executors.newSingleThreadExecutor();
public CreationReceipt<Attachment> createNew(Attachment attachment) {
String location = computeLocationFor(attachment);
Future<Attachment> savedAttachment = saveAsynchronously(attachment);
return new CreationReceipt<>(location, savedAttachment);
}
private Future<Attachment> saveAsynchronously(Attachment attachment) {
return executor.submit(() -> save(attachment));
}
private Attachment save(Attachment attachment) { ... }
private String computeLocationFor(Attachment attachment) { ... }
}
请注意,此实现一次只允许保存一个 Attachment
(使用 Executors.newSingleThreadExecutor()
的性质),但可以通过定义 executor
来更改该逻辑以使用不同的 ExecutorService
实现。要完成此实现,只需为同步保存新 Attachment
对象的 save
方法添加一个实现(此同步逻辑应在您现有的 createNew
方法中找到 AttachmentService
).
从这一点来看,保存 Attachment
的逻辑将是:
List<String> savedLocations = new ArrayList<>();
for (Tiedosto tiedosto : tiedostoList) {
Attachment attachment = new Attachment();
attachment.setCustomerId(tiedosto.getCustomerId());
attachment.setSize(tiedosto.getFileSize());
CreationReceipt<Attachment> receipt = attachmentService.createNew(attachmentToSave);
savedLocations.add(receipt.getLocation());
}
// Do something with savedLocations
这是一个不完整的实现,但它应该演示将要使用的基本结构。循环完成后,您可以将 saveLocations
作为响应的主体并将响应状态代码设置为 201 Created
.