使用链接响应标头的分页调用中的 Spring Flux

Posted

技术标签:

【中文标题】使用链接响应标头的分页调用中的 Spring Flux【英文标题】:Spring Flux from paginated calls using Link response header 【发布时间】:2021-02-25 10:57:51 【问题描述】:

对响应式编程和 Spring webclient 来说相当新,所以我试图了解它,本质上我有一个分页的结果列表,可以从 REST 服务使用,该服务使用带有相对 URI 的链接响应标头进行响应下一页结果(如果存在)。我想将所有页面收集到 Flux 中。默认情况下,生成的 ApiClient 是这样的

public <T> Flux<T> invokeFluxAPI(String path, HttpMethod method, Map<String, Object> pathParams,
        MultiValueMap<String, String> queryParams, Object body, HttpHeaders headerParams,
        MultiValueMap<String, String> cookieParams, MultiValueMap<String, Object> formParams,
        List<MediaType> accept, MediaType contentType, String[] authNames, ParameterizedTypeReference<T> returnType)
        throws RestClientException 
    final WebClient.RequestBodySpec requestBuilder = prepareRequest(path, method, pathParams, queryParams, body,
            headerParams, cookieParams, formParams, accept, contentType, authNames);
    return requestBuilder.retrieve().bodyToFlux(returnType);

那么我是否需要对结果的每一页进行某种递归调用,将每一页 Mono 组合成一个通量?例如。沿着

public <T> Flux<T> invokeFluxAPI(String path, HttpMethod method, Map<String, Object> pathParams,
        MultiValueMap<String, String> queryParams, Object body, HttpHeaders headerParams,
        MultiValueMap<String, String> cookieParams, MultiValueMap<String, Object> formParams,
        List<MediaType> accept, MediaType contentType, String[] authNames, ParameterizedTypeReference<T> returnType)
        throws RestClientException 
    final WebClient.RequestBodySpec requestBuilder = prepareRequest(path, method, pathParams, queryParams, body,
            headerParams, cookieParams, formParams, accept, contentType, authNames);
    return requestBuilder.exchange()
            .doOnSuccess(response -> 
                List<String> links = response.headers().header("Link");
                if (!links.isEmpty()) 
                    String nextPageUri = links.get(0);
                    // Get next page using the URI
                
            )
            .flatMapMany(response -> response.bodyToFlux(returnType));

【问题讨论】:

【参考方案1】:

所以我设法通过将ClientResponse 视为结果页面来实现此功能。

public <T> Flux<T> invokeFluxAPI(String path, HttpMethod method, Map<String, Object> pathParams,
        MultiValueMap<String, String> queryParams, Object body, HttpHeaders headerParams,
        MultiValueMap<String, String> cookieParams, MultiValueMap<String, Object> formParams,
        List<MediaType> accept, MediaType contentType, String[] authNames, ParameterizedTypeReference<T> returnType)
        throws RestClientException 
    return fetchResultsPage(path, method, pathParams, queryParams, body, headerParams, cookieParams, formParams, accept, contentType, authNames).expand(response -> 
        List<String> links = response.headers().header("Link");
        if (links.isEmpty()) 
            return Mono.empty();
        

        Matcher m = "(?<=\\<)([^\\]]+)(?=\\>)".matcher(links.get(0));

        if (!m.find())) 
            return Mono.empty();
        

        String nextPageUri = m.group(1);

        MultiValueMap<String, String> qParams = UriComponentsBuilder.fromUriString(nextPageUri).build().getQueryParams();

        queryParams.putAll(qParams);
        queryParams.replace("limit", qParams.get("limit"));

        return fetchResultsPage(path, method, pathParams, queryParams, body, headerParams, cookieParams, formParams, accept, contentType, authNames);
    ).flatMap(response -> response.bodyToFlux(returnType);


private Mono<ClientResponse> fetchResultsPage(String path, HttpMethod method, Map<String, Object> pathParams,
        MultiValueMap<String, String> queryParams, Object body, HttpHeaders headerParams,
        MultiValueMap<String, String> cookieParams, MultiValueMap<String, Object> formParams,
        List<MediaType> accept, MediaType contentType, String[] authNames) 
    final WebClient.RequestBodySpec requestBuilder = prepareRequest(path, method, pathParams, queryParams, body,
            headerParams, cookieParams, formParams, accept, contentType, authNames);
    return requestBuilder.exchange();

【讨论】:

以上是关于使用链接响应标头的分页调用中的 Spring Flux的主要内容,如果未能解决你的问题,请参考以下文章

附加到 Laravel 中的分页链接 URL

Spring中的分页

为 ajax 响应自定义 DataTables 分页

Spring Data Cassandra 中的分页和排序查询

如何使用 Spring Boot WebClient 收集分页 API 响应?

Spring Data JPA中的分页问题