我需要从可分页REST API的所有页面获取项目。我还需要开始处理项目,只要它们可用,不需要等待所有页面加载。为了做到这一点,我使用了Spring WebFlux及其WebClient,并希望返回Flux
对单个页面请求的响应如下所示:
{
"data": [],
"meta": {
"pagination": {
"total": 10,
"current": 1
}
}
}
数据数组包含实际项目,而元对象包含分页信息。
我当前的解决方案首先执行一个“虚拟”请求,只是为了获取总页数和速率限制。
Mono<T> paginated = client.get()
.uri(uri)
.exchange()
.flatMap(response -> {
HttpHeaders headers = response.headers().asHttpHeaders();
Limits limits = new Limits();
limits.setWindowSize(headers.getFirst("X-Window-Size"));
limits.setWindowRemaining(headers.getFirst("X-Window-Remaining"));
limits.setRequestsQuota(headers.getFirst("X-Requests-Quota");
limits.setRequestsLeft(headers.getFirst("X-Requests-Remaining");
return response.bodyToMono(Paginated.class)
.map(paginated -> {
paginated.setLimits(limits);
return paginated;
});
});
之后,我发出一个包含页码的流量,对于每个页面,我执行一个REST API请求,每个请求都被延迟了足够长的时间,因此不会超过限制,并返回一个提取项目的流量:
return paginated.flatMapMany(paginated -> {
return Flux.range(1, paginated.getMeta().getPagination().getTotal())
.delayElements(Duration.ofMillis(paginated.getLimits().getWindowRemaining() / paginated.getLimits().getRequestsQuota()))
.flatMap(page -> {
return client.get()
.uri(pageUri)
.retrieve()
.bodyToMono(Item.class)
.flatMapMany(p -> Flux.fromIterable(p.getData()));
});
});
这确实有效,但我对此不满意,因为:
因此,我的问题是如何重构它,使其不需要初始请求(而是从第一个请求中获取限制、页码和数据,并继续遍历所有页面,同时更新(并尊重)限制)。
我认为这段代码可以满足您的要求。其想法是创建一个流量来调用资源服务器,但在处理响应的过程中,在该流量上添加一个新事件,以便能够调用下一页。
代码由以下部分组成:
一个简单的包装器,包含要调用的下一页和在执行调用之前要等待的延迟
private class WaitAndNext{
private String next;
private long delay;
}
一个将进行HTTP调用并处理响应的FLuxProcess:
FluxProcessor<WaitAndNext, WaitAndNext> processor= DirectProcessor.<WaitAndNext>create();
FluxSink<WaitAndNext> sink=processor.sink();
processor
.flatMap(x-> Mono.just(x).delayElement(Duration.ofMillis(x.delay)))
.map(x-> WebClient.builder()
.baseUrl(x.next)
.defaultHeader("Accept","application/json")
.build())
.flatMap(x->x.get()
.exchange()
.flatMapMany(z->manageResponse(sink, z))
)
.subscribe(........);
我用一个只管理响应的方法拆分代码:它只是打开您的数据并向接收器添加一个新事件(在给定延迟后调用下一页的事件)
private Flux<Data> manageResponse(FluxSink<WaitAndNext> sink, ClientResponse resp) {
if (resp.statusCode()!= HttpStatus.OK){
sink.error(new IllegalStateException("Status code invalid"));
}
WaitAndNext wn=new WaitAndNext();
HttpHeaders headers=resp.headers().asHttpHeaders();
wn.delay= Integer.parseInt(headers.getFirst("X-Window-Remaining"))/ Integer.parseInt(headers.getFirst("X-Requests-Quota"));
return resp.bodyToMono(Item.class)
.flatMapMany(p -> {
if (p.paginated.current==p.paginated.total){
sink.complete();
}else{
wn.next="https://....?page="+(p.paginated.current+1);
sink.next(wn);
}
return Flux.fromIterable(p.getData());
});
}
现在我们只需要通过无延迟地调用第一页的检索来初始化系统:
WaitAndNext wn=new WaitAndNext();
wn.next="https://....?page=1";
wn.delay=0;
sink.next(wn);
问题内容: 我正在使用php,mysql进行搜索,过滤操作。 我的分页课程是 我已经使用以下复选框执行了过滤器: 过滤器的Javascript / ajax代码 process.php文件 我返回分页的结果,但是当我单击页码时,将我带到process.php,因为分页类使用 $ _SERVER [PHP_SELF] 如何在不更改页面url的情况下对结果进行分页,即使用ajax实施。我不能做太多更改
问题内容: 我将GWT用于UI,将Hibernate / Spring用于业务层。以下GWT小部件用于显示记录。(http://collectionofdemos.appspot.com/demo/com.google.gwt.gen2.demo.scrolltable.PagingScrollTableDemo /PagingScrollTableDemo.html )。我认为排序是在客户端完成
客户端分享 对接前提 1.需要APP本身有客户端分享功能和对应的分享接口 2.在管理后台配置好并开启分享功能 分享定制 如果开发者已有自己的一套H5分享接口,可以联系兑吧技术支持,由技术支持根据分享接口要求做定制。请联系兑吧技术支持协助配置。 分享案例 1.客户端分享兑吧活动 2.分享结果 3.点击分享跳转兑吧活动页 4.点击参与跳转下载页
问题内容: 我想基于其api实现Google地图。我想添加一个基于坐标的路径。因此,我从模型中获取了坐标,并希望在对象上进行迭代以用此点填充地图。在我的Jade模板中,我包含api js代码,如下所示: 问题是:玉呈现此片段 就像在jade模板源中一样…-如果不被解析!有任何想法吗? 谢谢! 问题答案: 整个脚本标签(在其下缩进的所有内容)将通过原始文件传递,而无需进一步解析。Jade不会HTML
Java import java.io.IOException; import java.net.URLEncoder; import java.security.MessageDigest; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import jav
我想在一些计算机之间建立点对点连接,这样用户就可以在没有外部服务器的情况下聊天和交换文件。我最初的想法如下: 我在服务器上制作了一个中央服务器插座,所有应用程序都可以连接到该插座。此ServerSocket跟踪已连接的套接字(客户端),并将新连接的客户端的IP和端口提供给所有其他客户端。每个客户端都会创建一个新的ServerSocket,所有客户端都可以连接到它。 换句话说:每个客户端都有一个Se