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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 22:27:45