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
相关产品推荐
相关产品推荐

