使用Spring声明式REST Client以Reader作为POST请求体遇错求解
解决方案
方案1:自定义消息转换器(推荐大文件场景)
Spring WebFlux客户端默认没有提供处理Reader类型的消息转换器,这是导致UnsupportedMediaTypeException的核心原因。我们可以自定义一个HttpMessageWriter来支持将Reader内容写入请求体,并注册到声明式REST Client的配置中。
实现自定义HttpMessageWriter
import org.springframework.core.ResolvableType; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ReactiveHttpOutputMessage; import org.springframework.http.codec.HttpMessageWriter; import reactor.core.publisher.Mono; import java.io.Reader; import java.nio.CharBuffer; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; import java.util.Map; public class ReaderHttpMessageWriter implements HttpMessageWriter<Reader> { @Override public boolean canWrite(ResolvableType elementType, MediaType mediaType) { return Reader.class.isAssignableFrom(elementType.toClass()) && (mediaType == null || MediaType.TEXT_PLAIN.isCompatibleWith(mediaType)); } @Override public List<MediaType> getWritableMediaTypes() { return Collections.singletonList(MediaType.TEXT_PLAIN); } @Override public Mono<Void> write(Publisher<? extends Reader> inputStream, ResolvableType elementType, MediaType mediaType, ReactiveHttpOutputMessage message, Map<String, Object> hints) { return Mono.from(inputStream) .flatMap(reader -> { DataBufferFactory bufferFactory = message.bufferFactory(); HttpHeaders headers = message.getHeaders(); headers.setContentType(MediaType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8)); CharBuffer charBuffer = CharBuffer.allocate(8192); return Mono.create(sink -> { try { int read; while ((read = reader.read(charBuffer)) != -1) { charBuffer.flip(); DataBuffer buffer = bufferFactory.wrap(charBuffer.toString().getBytes(StandardCharsets.UTF_8)); message.writeWith(Mono.just(buffer)).subscribe(); charBuffer.clear(); } reader.close(); sink.success(); } catch (Exception e) { sink.error(e); } }); }) .then(message.setComplete()); } }
注册转换器到RestClient
创建客户端实例时,添加自定义消息转换器:
MyRestClient restClient = RestClient.builder() .baseUrl("http://your-api-host") .messageWriters(writers -> writers.add(new ReaderHttpMessageWriter())) .build() .create().client(MyRestClient.class);
方案2:将Reader转为String(小文件场景适用)
如果处理的文本内容不大,可以先把Reader的内容读取为String,利用Spring默认的字符串消息转换器处理请求体:
修改接口定义
@HttpExchange public interface MyRestClient { @PostExchange(value = "/postString") int postTextFile(@RequestBody String body); }
调用时转换Reader到String
import java.io.BufferedReader; import java.io.FileReader; import java.io.Reader; import java.util.stream.Collectors; // 示例:从Reader读取内容到String Reader reader = new FileReader("path/to/your/file.txt"); String content = new BufferedReader(reader) .lines() .collect(Collectors.joining(System.lineSeparator())); reader.close(); // 调用接口 restClient.postTextFile(content);
注意:该方式会将整个文本加载到内存,大文件场景不建议使用。
方案3:使用Resource替代Reader(推荐文件场景)
Spring WebFlux客户端原生支持org.springframework.core.io.Resource类型作为请求体,你可以将文件包装成FileSystemResource,无需自定义转换器:
修改接口定义
import org.springframework.core.io.Resource; import org.springframework.web.service.annotation.HttpExchange; import org.springframework.web.service.annotation.PostExchange; @HttpExchange public interface MyRestClient { @PostExchange(value = "/postString") int postTextFile(@RequestBody Resource body); }
调用时传入Resource实例
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; // 包装文件为Resource Resource fileResource = new FileSystemResource("path/to/your/file.txt"); // 调用接口 restClient.postTextFile(fileResource);
Spring会自动处理内容类型设置和流的写入,是文件上传场景最简便的方案。
内容的提问来源于stack exchange,提问作者dash1e
相关产品推荐
相关产品推荐

