-
Notifications
You must be signed in to change notification settings - Fork 38.9k
Description
** Bug Reports /Enhancements requests **
Java Version : 25
Spring Boot Version : 4.0.1
Spring WebClient with http interface throws UnsupportedMediaTypeException when Object class is used as RequestBody and when Content-Type is 'application/xml'
The same works when used with Spring RestClient for any content type. This also works with Webclient with content type as 'application/json'.
The issue happening because the RequestBody type resolution is happening at the time of Interface proxy creation rather than when actual request is fired. The org.springframework.web.service.invoker.ReactiveHttpRequestValues.bodyValueType is getting populated as java.lang.Object rather than the actual request body class type.
Here are some test classes used to identify the issue.
public class WebClientTest {
public interface NameInterfaceReactive {
@PostExchange("test")
public Mono<ResponseEntity<Response>> sendRequest(@RequestBody final Object request);
}
public static void main(String[] args) throws InterruptedException {
NameInterfaceReactive nameInterfaceReactive = getNameInterfaceReactive();
System.out.println("nameInterfaceReactive "+nameInterfaceReactive.getClass().getName());
nameInterfaceReactive.sendRequest(new Request("John", "Rambo")).subscribe(res -> System.out.println(res));
Thread.sleep(Duration.ofSeconds(50));
}
public static NameInterfaceReactive getNameInterfaceReactive() {
final WebClient.Builder builder = WebClient.builder();
builder.clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
builder.baseUrl("http://127.0.0.1:8080/");
builder.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE);
final WebClient webClient = builder.build();
final HttpServiceProxyFactory httpServiceProxyFactory = HttpServiceProxyFactory.builderFor(WebClientAdapter.create(webClient)).build();
final NameInterfaceReactive nameInterfaceReactive = httpServiceProxyFactory.createClient(NameInterfaceReactive.class);
return nameInterfaceReactive;
}
}
This works with RestClient
public class RestClientTest {
public interface NameInterface {
@PostExchange("test")
public ResponseEntity<Response> sendRequest(@RequestBody final Object request);
}
public static void main(String[] args) {
NameInterface nameInterface = getNameInterface();
System.out.println(nameInterface.sendRequest(new Request("John", "Rambo")).getBody());
}
public static NameInterface getNameInterface() {
final RestClient.Builder builder = RestClient.builder();
builder.baseUrl("http://127.0.0.1:8080/");
builder.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE);
final RestClient restClient = builder.build();
final HttpServiceProxyFactory httpServiceProxyFactory = HttpServiceProxyFactory.builderFor(RestClientAdapter.create(restClient)).build();
NameInterface nameInterface = httpServiceProxyFactory.createClient(NameInterface.class);
return nameInterface;
}
}
Here is full exception.
Caused by: org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/xml' not supported for bodyType=java.lang.Object
at org.springframework.web.reactive.function.BodyInserters.unsupportedError(BodyInserters.java:445)
at org.springframework.web.reactive.function.BodyInserters.writeWithMessageWriters(BodyInserters.java:435)
at org.springframework.web.reactive.function.BodyInserters.lambda$fromValue$1(BodyInserters.java:129)
at org.springframework.web.reactive.function.client.DefaultClientRequestBuilder$BodyInserterRequest.writeTo(DefaultClientRequestBuilder.java:273)
at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$exchange$0(ExchangeFunctions.java:103)
at org.springframework.http.client.reactive.ReactorClientHttpConnector.lambda$connect$0(ReactorClientHttpConnector.java:166)
at reactor.netty.http.client.HttpClientConnect$HttpClientHandler.requestWithBody(HttpClientConnect.java:657)
at reactor.netty.http.client.HttpClientConnect$HttpIOHandlerObserver.lambda$onStateChange$0(HttpClientConnect.java:469)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:46)
at reactor.netty.http.client.HttpClientConnect$HttpIOHandlerObserver.onStateChange(HttpClientConnect.java:470)
at reactor.netty.ReactorNetty$CompositeConnectionObserver.onStateChange(ReactorNetty.java:730)
at reactor.netty.resources.DefaultPooledConnectionProvider$DisposableAcquire.onStateChange(DefaultPooledConnectionProvider.java:215)
at reactor.netty.resources.DefaultPooledConnectionProvider$PooledConnection.onStateChange(DefaultPooledConnectionProvider.java:477)
at reactor.netty.channel.ChannelOperationsHandler.channelActive(ChannelOperationsHandler.java:62)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelActive(AbstractChannelHandlerContext.java:220)
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelActive(CombinedChannelDuplexHandler.java:410)
at io.netty.channel.ChannelInboundHandlerAdapter.channelActive(ChannelInboundHandlerAdapter.java:69)
at io.netty.channel.CombinedChannelDuplexHandler.channelActive(CombinedChannelDuplexHandler.java:209)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelActive(AbstractChannelHandlerContext.java:220)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelActive(DefaultChannelPipeline.java:1417)
at io.netty.channel.DefaultChannelPipeline.fireChannelActive(DefaultChannelPipeline.java:862)
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.fulfillConnectPromise(AbstractNioChannel.java:355)
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:385)
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.handle(AbstractNioChannel.java:432)
at io.netty.channel.nio.NioIoHandler$DefaultNioRegistration.handle(NioIoHandler.java:388)
at io.netty.channel.nio.NioIoHandler.processSelectedKey(NioIoHandler.java:596)
at io.netty.channel.nio.NioIoHandler.processSelectedKeysPlain(NioIoHandler.java:541)
at io.netty.channel.nio.NioIoHandler.processSelectedKeys(NioIoHandler.java:514)
at io.netty.channel.nio.NioIoHandler.run(NioIoHandler.java:484)
at io.netty.channel.SingleThreadIoEventLoop.runIo(SingleThreadIoEventLoop.java:225)
at io.netty.channel.SingleThreadIoEventLoop.run(SingleThreadIoEventLoop.java:196)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:1193)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:1474)