Spring Boot中WebClient.block()获取大JSON被截断缺失的原因是什么?
核心疑问解答
block()的作用仅为阻塞当前线程等待WebClient的异步响应读取流程结束,并不会控制响应内容的读取长度。你遇到的固定长度截断是Spring WebClient内置解码器的默认最大内存缓冲区限制导致:Spring Boot 2.4.x版本对应的Spring Webflux默认单请求最大内存缓冲区为4MB(和你遇到的4193150字符阈值完全吻合,UTF-8编码下1个英文字符占1字节,4MB约等于4194304字节),响应超过该阈值后,解码器会直接截断不再读取后续内容,因此你拿到的JSON字符串本身就是不完整的,和是否调用block()没有关系。
代码存在的其他问题
- 冗余编码转换:你将响应转为String后又调用
getBytes("UTF-8")再重新转回UTF-8字符串,属于完全无效的操作,不会解决任何问题,反而会额外占用内存。 - ObjectMapper配置顺序错误:你先执行
readValue解析JSON,再配置FAIL_ON_UNKNOWN_PROPERTIES=false,该配置对前面的解析操作完全不生效,相当于无用代码。 - 字符串取值逻辑错误:调用
nameNode.toString()、mainNode.get("id").toString()会把JSON字符串值包裹上双引号,你后续用replaceAll("\"", "")删除引号属于多此一举,直接调用asText()方法就能拿到原生字符串值。 - 重复创建ObjectMapper:每次方法调用都新建ObjectMapper实例是严重的性能浪费,ObjectMapper是线程安全的,完全可以全局初始化复用。
修复方案
- 调整WebClient最大内存缓冲区限制,有两种可选方式:
- 全局配置:在
application.properties/application.yml中添加配置spring.codec.max-in-memory-size=10MB(可根据实际响应大小调整阈值) - 单独为WebClient实例配置:
WebClient webClient = WebClient.builder() .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(10 * 1024 * 1024)) // 设置为10MB .build(); - 全局配置:在
- 优化后的代码示例:
// 全局复用配置好的ObjectMapper private final ObjectMapper objectMapper = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); private List<MyPojo> recursiveRestCallMethod(String folderId) throws IOException { List<MyPojo> mypojoList = new ArrayList<>(); // 直接将响应反序列化为ObjectNode,不需要中间转String ObjectNode node = webClient.get() .uri("/my/rest/api/accepting/{folderId}/and/producing/huge/jsonresponse/for/all/files/recursively", folderId) .retrieve() .bodyToMono(ObjectNode.class) .block(); if (node == null) { return mypojoList; } JsonNode entriesNode = node.get("list").get("entries"); if (entriesNode.isArray()) { for (JsonNode jsonNode : entriesNode) { JsonNode mainNode = jsonNode.get("entry"); if (mainNode.get("isFile").asBoolean()) { MyPojo pojo = new MyPojo(); pojo.setNodename(mainNode.get("name").asText()); // 其余20个字段赋值 mypojoList.add(pojo); } if (mainNode.get("isFolder").asBoolean()) { String subFolderId = mainNode.get("id").asText().trim(); mypojoList.addAll(recursiveRestCallMethod(subFolderId)); } } } return mypojoList; }
- 可选优化:如果响应体积会持续增长,建议改用流式反序列化处理JSON,不需要一次性加载整个JSON到内存,避免出现OOM问题。
内容的提问来源于stack exchange,提问作者Ajay Kumar
相关产品推荐
相关产品推荐

