** 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)
** 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.
This works with RestClient
Here is full exception.