当前位置: 首页 > 知识库问答 >
问题:

为什么通过ServerResponse返回的flux会折叠为单个字符串,除非每个字符串元素都以“\n”结尾?

曾光远
2023-03-14

在使用Spring Webflux和WebClient时,我注意到当返回包含Flux 的ServerResponse时的一个行为(由下面的html" target="_blank">代码演示)。除非字符串元素以换行符结尾,否则通过ServerResponse返回flux 似乎会将所有flux 元素连接到单个字符串中。有人能解释一下为什么我会看到这种行为,以及我是怎么做的导致这种行为的吗?

当每个字符串元素以换行符终止时,flux 将通过ServerResponse“按预期返回”,订阅返回的flux 将产生预期的结果。但是,如果将其视为简单的JSON(通过Postman),这也会导致在JSON主体中返回一个额外的空字符串元素。

控制台输出显示描述的行为...

>

  • String元素的第一个列表出现在StringProducerHandler.getAll()中,并指示包含10个String元素的通量 的结果,其中每个String值的第二个出现以换行符结束,结果输出一个空行。
  • String元素的第二个列表出现在StringClient.getAll()中,并演示了未用换行符终止的原始String元素如何与下面的元素连接。

    2019-10-10 10:13:37.225信息8748---[main]O.s.B.Web.Embedded.Netty.NettyWebServer:Netty在端口上启动:8080 2019-10-10 10:13:37.228信息8748---[main]C.Example.FluxTest.FluxTestApplication:在1.271秒内启动了FluxTestApplication(JVM运行时间为1.796)

    *****“Get”在http://localhost:8080/StringClient/StringClientHandler.getAll(ServerRequest)StringClient.getAll()StringProducerHandler.getAll(ServerRequest)ListElement-0 ListElement-0

    列表-1列表-1

    列表元素-2列表元素-2

    清单-3清单-3

    列表-4列表-4

    ListElement-0ListElement-0@1570727628948 ListElement-1ListElement-1@1570727628948 ListElement-2ListElement-2@1570727628948 ListElement-3ListElement-3@1570727628948 ListElement-4ListElement-4@1570727628949

    下面提供了复制此行为的代码...

    @SpringBootApplication
    public class FluxTestApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(FluxTestApplication.class, args);
        }
    }
    
    
    @Configuration
    public class StringClientRouter {
        @Bean
        public RouterFunction<ServerResponse> clientRoutes(StringClientHandler requestHandler) {
            return nest(path("/StringClient"),
                    nest(accept(APPLICATION_JSON),
                            RouterFunctions.route(RequestPredicates.GET("/String"), requestHandler::getAll)));
        }
    }
    
    
    @Component
    public class StringClientHandler {
    
        @Autowired
        StringClient stringClient;
    
        public Mono<ServerResponse> getAll(ServerRequest request) {
            System.out.println("StringClientHandler.getAll( ServerRequest )");
    
            Mono<Void> signal = stringClient.getAll();
                return ServerResponse.ok().build();
            }
        }
    
    
    @Component
    public class StringClient {
    
        private final WebClient client;
    
        public StringClient() {
            client = WebClient.create();
        }
    
        public Mono<Void> getAll() {
            System.out.println("StringClient.getAll()");
    
            // break chain to explicitly obtain the ClientResponse
            Mono<ClientResponse> monoCR = client.get().uri("http://localhost:8080/StringProducer/String")
                                                      .accept(MediaType.APPLICATION_JSON)
                                                      .exchange();
    
            // extract the Flux<String> and print to console
            Flux<String> fluxString = monoCR.flatMapMany(response -> response.bodyToFlux(String.class));
            // this statement iterates over the Flux<String> and outputs each element
            fluxString.subscribe(strVal -> System.out.println(strVal + " @ " + System.currentTimeMillis()));
    
            return Mono.empty();
        }
    }
    
    
    @Configuration
    public class StringProducerRouter {
        @Bean
        public RouterFunction<ServerResponse> demoPOJORoute(StringProducerHandler requestHandler) {
            return nest(path("/StringProducer"),
                    nest(accept(APPLICATION_JSON),
                            RouterFunctions.route(RequestPredicates.GET("/String"), requestHandler::getAll)));
        }
    }
    
    
    @Component
    public class StringProducerHandler {
    
        public Mono<ServerResponse> getAll(ServerRequest request) {
            System.out.println("StringProducerHandler.getAll( ServerRequest )");
    
            int          listSize = 5;
            List<String> strList  = new ArrayList<String>();
    
            for (int i=0; i<listSize; i++) {
                strList.add("ListElement-" + i);         // add String value without newline termination
                strList.add("ListElement-" + i + "\n");  // add String value with    newline termination
            }
    
            // this statement produces the expected console output of String values
            Flux.fromIterable(strList).subscribe(System.out::println);
    
            return ServerResponse.ok()
                                 .contentType(MediaType.APPLICATION_JSON)
                                 .body(Flux.fromIterable(strList), String.class);
        }
    }
    
  • 共有1个答案

    韩梓
    2023-03-14

    这是由于org.springframework.core.codec.stringdecoder的工作方式。

    调用response.BodyToFlux(string.class)时,响应体将转换为StringFluxorg.springframework.core.codec.StringDecoder完成了很大的任务,并且认为它应该拆分默认分隔符。

    List<byte[]> delimiterBytes = getDelimiterBytes(mimeType);
    
    Flux<DataBuffer> inputFlux = Flux.from(input)
        .flatMapIterable(buffer -> splitOnDelimiter(buffer, delimiterBytes))
        .bufferUntil(buffer -> buffer == END_FRAME)
        .map(StringDecoder::joinUntilEndFrame)
        .doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
    
    return super.decode(inputFlux, elementType, mimeType, hints);
    

    默认分隔符为public static final list DEFAULT_DELIMITERS=arrays.aslist(“\r\n”,“\n”);

    因此你得到:

    ListElement-0ListElement-0 @ 1570732000374
    ListElement-1ListElement-1 @ 1570732000375
    ...
    

    而不是

    ListElement-0 @ 1570732055461
    ListElement-0 @ 1570732055461
    ListElement-1 @ 1570732055462
    ListElement-1 @ 1570732055462
    ...
    
     类似资料:
    • 问题内容: 例如: 问题答案: 这是因为从索引0 in开始的长度为0的子字符串等于空字符串: 当然,任何字符串的长度为零的 每个 子字符串都等于空字符串。

    • 问题内容: 我有一个带有3个嵌套数组的简单php结构。 我不使用特定的对象,而是使用2个嵌套循环构建数组。 这是我要转换为Json的数组的var_dump的示例。 在另一个脚本中,我具有类似的结构并且工作正常。所以我不明白为什么在这里不起作用。 编辑:似乎有编码问题。当返回ASCII,该作品但当它返回UTF8,它不工作了。 Edit2:返回表示:格式错误的UTF-8字符,可能编码不正确。 问题答案

    • 问题内容: 这可能是最简单的事情之一,但我看不到自己在做错什么。 我的输入包括一个带有数字的第一行(要读取的行数),一串包含数据的行和最后一行仅包含\ n的行。我应该处理此输入,并在最后一行之后做一些工作。 我有这个输入: 对于读取输入,我有以下代码。 我的问题是为什么我什么都不打印?程序读取第一行,然后不执行任何操作。 问题答案: 不读取以下换行符,因此第一个(返回 当前 行的其余部分 )将始终

    • 为什么返回而不是像或这样的较小类型?我的理解是,这个方法只返回-1、0或1。 第二部分,如果我要设计一个比较方法来比较两个类型的对象,并且我只想返回-1、0或1,那么使用或通常是个好主意吗? 编辑:我已经更正,不返回-1,0或1,它实际上返回一个值 答案似乎大致是,没有理由返回小于的类型,因为返回值是右值,而这些右值不会受益于小于int类型(4字节)。此外,许多人指出,大多数系统的寄存器可能会有大

    • 问题内容: 这是有效的,并返回JavaScript中的字符串 为什么?这是怎么回事 问题答案: 如果我们将其拆分,则混乱等于: 在JavaScript中,确实是这样。将某物转换为数字,在这种情况下,它将降为或(请参见下面的规范详细信息)。 因此,我们可以简化它(优先于): 因为意思是:从中获取第一个元素,所以确实: 返回内部数组()。由于引用,说错了,但是让我们调用内部数组以避免错误的表示法。 在

    • 问题内容: 是否可以每个字符分割一个字符串? 例如,假设我有一个包含以下内容的字符串: 我怎样才能使它看起来像这样: 问题答案: 192 为了完整起见,你可以使用正则表达式执行此操作: 对于字符的奇数,你可以执行以下操作: 你还可以执行以下操作,以简化较长块的正则表达式: re.finditer如果字符串很长,则可以使用它逐块生成。