为什么在RestController中,Flux.fromIterable()作为一个串联字符串返回?

时间:2019-02-24 21:42:32

标签: java spring spring-boot reactive-programming project-reactor

我有一个Rest Conotroller,它返回一个Flux<String>,但是当我尝试将其收集到一个列表中时,它是所有串联字符串中的一项。如何获得它作为实际列表?

控制器:

@RestController
public class TestRestController
{
    @GetMapping( "/getflux" )
    public Flux<String> getFlux()
    {
        return Flux.fromIterable(
            Arrays.asList(
                "String 1", 
                "String 2"
            )
        );
    }
}

呼叫控制器:

//This returns as a list of one item: "String 1String 2
List<String> response = WebClient.builder()
    .baseUrl( "http://localhost:" + port + "/" )
    .build()
    .get()
    .uri( "/getflux" )
    .retrieve()
    .bodyToFlux( String.class )
    .collectList()
    .block();

如何获取实际列表?

2 个答案:

答案 0 :(得分:1)

似乎是List<String>反序列化的问题(例如List<Integer>和许多其他类型都可以正常工作)。我试图调整Jackson的ObjectMapper配置,但失败了。也许您也应该自己尝试一下,甚至将问题提交给Jackson Github回购。

作为一种解决方法,您可以从控制器方法中返回Mono<List<String>>

@GetMapping("/getflux")
public Mono<List<String>> getFlux() {
    return Flux.fromIterable(
            Arrays.asList(
                    "String 1",
                    "String 2"

            )
    ).collectList();
}

,然后这样称呼它:

List<String> block = WebClient.builder()
    .baseUrl("http://localhost:" + 8080 + "/")
    .build()
    .get()
    .uri("/getflux")
    .retrieve()
    .bodyToMono(new ParameterizedTypeReference<List<String>>() {
    })
    .block();

结果为["String 1","String 2"]。直接从Web浏览器/ curl / etc中调用controller方法将返回相同的结果。

答案 1 :(得分:1)

经过研究,看来这是一个被标记为“按预期工作”的Spring错误。

https://github.com/spring-projects/spring-framework/issues/20807

  

这是预期的行为。默认情况下,字节数组,字节缓冲区和字符串被视为低级内容(序列化输出),并按原样呈现。实际上,Flux会流传输,并且每个字符串都会立即写入并刷新。

     

Jackson编码器显式退出了元素类型String。我意识到String和String的数组可以呈现为JSON,但是有两种处理String内容的方式,这是我们默认选择的方式。

唯一的解决方案是从不返回Flux<String>,而是返回您创建的某些包装器类的列表。这样仍然可以使用Flux和背压,并且Spring可以正确处理此类复杂的对象。

以下内容可完美运行:

@GetMapping("/getflux")
public Flux<List<StringWrapper>> getFlux() {
    return Flux.fromIterable(
        Arrays.asList(
            new StringWrapper( "String 1" ),
            new StringWrapper( "String 2" )

        )
    );
}