如何在 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();
}

tiedostoServiceattachmentService 为两个 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.