You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中如何使用WebClient消费二进制与字符串混合响应

问题原因

WebClient 默认的消息转换器仅支持将 JSON 类型响应反序列化为 Object 类型,当接口返回 application/pdf/图片这类二进制流时,没有对应的转换器支持将流转为 Object,因此抛出类型不支持的错误。

解决方案

方案1:统一以字节数组接收响应,后续按 Content-Type 区分处理

这是兼容性最高的方案,不管返回的是二进制文件还是 JSON 都可以正常接收,之后根据响应头的 Content-Type 做差异化处理即可:

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.*;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import java.io.FileOutputStream;
import java.net.URI;

// 先自定义错误响应实体类,和接口返回的错误JSON结构对应
class ErrorResp {
    private Integer code;
    private String msg;
    // 省略getter/setter
}

public class ApiTest {
    public void requestApi(MultiValueMap<String, String> map) throws Exception {
        WebClient webClient = WebClient.create();
        ResponseEntity<byte[]> apiResponse = webClient.post()
                .uri(new URI("https://api.myapp.in/getDocument"))
                .header("mobile", "XXXXXXXXX8")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                // 配置接受所有支持的返回类型
                .accept(MediaType.APPLICATION_PDF, MediaType.IMAGE_JPEG, MediaType.IMAGE_PNG, MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromFormData(map))
                .retrieve()
                .toEntity(byte[].class)
                .block();

        MediaType contentType = apiResponse.getHeaders().getContentType();
        byte[] body = apiResponse.getBody();
        if (contentType != null && contentType.includes(MediaType.APPLICATION_JSON)) {
            // 错误场景:把字节数组转成错误JSON对象
            ObjectMapper objectMapper = new ObjectMapper();
            ErrorResp errorResp = objectMapper.readValue(body, ErrorResp.class);
            // 自行处理错误逻辑
            System.out.println("接口报错:" + errorResp.getMsg());
        } else {
            // 成功场景:把字节数组存为对应格式的文件
            String suffix = contentType == null ? "pdf" : contentType.getSubtype();
            FileOutputStream fos = new FileOutputStream("document." + suffix);
            fos.write(body);
            fos.close();
        }
    }
}

方案2:根据响应状态码自定义处理逻辑(推荐)

如果接口约定了 2xx 状态码返回文件、非 2xx 状态码返回 JSON 错误,直接用 exchangeToMono 分别处理两种场景即可,逻辑更清晰:

Object result = webClient.post()
        .uri(new URI("https://api.myapp.in/getDocument"))
        .header("mobile", "XXXXXXXXX8")
        .contentType(MediaType.APPLICATION_FORM_URLENCODED)
        .accept(MediaType.APPLICATION_PDF, MediaType.IMAGE_JPEG, MediaType.IMAGE_PNG, MediaType.APPLICATION_JSON)
        .body(BodyInserters.fromFormData(map))
        .exchangeToMono(response -> {
            if (response.statusCode().is2xxSuccessful()) {
                // 成功状态直接返回字节数组
                return response.bodyToMono(byte[].class);
            } else {
                // 错误状态直接反序列化为错误JSON对象
                return response.bodyToMono(ErrorResp.class);
            }
        })
        .block();

// 后续处理返回结果
if (result instanceof byte[]) {
    // 处理文件逻辑
} else if (result instanceof ErrorResp) {
    // 处理错误逻辑
}

注意事项

如果需要支持更多返回的文件格式,在 accept 方法中添加对应的 MediaType 即可。

内容的提问来源于stack exchange,提问作者Lalitkumar.annaldas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 10:06:04