Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to change content type using modifyResponseBody filter #1980

Closed
ghost opened this issue Oct 9, 2020 · 3 comments
Closed

Unable to change content type using modifyResponseBody filter #1980

ghost opened this issue Oct 9, 2020 · 3 comments

Comments

@ghost
Copy link

ghost commented Oct 9, 2020

I'm trying to create a route to a service that returns an XML response. I need to get some data from the response, and then use it to create a custom object which will be returned as JSON from the gateway. I'm trying to use the modifyResponseBody filter to do this, but it seems to be having an issue changing the content type. The filter takes "newContentType" as an argument, so I would except it to be able to change the content type of the response, similarly to how the content type can be changed with the modifyRequestBody filter which does seem to work properly.

I'm using spring cloud gateway version 2.2.5.RELEASE

Below is a code sample for the route which is using the filter, and below that is the stack trace of the error I'm seeing

@Bean
public RouteLocator billingServiceRoute(RouteLocatorBuilder builder) {
	return builder.routes()
		.route(r -> r.path("/billing-service")
			.filters(f ->
				f.modifyResponseBody(String.class, ServiceResponse.class, MediaType.APPLICATION_JSON_VALUE,
					((exchange, response) -> Mono.just(new ServiceResponse(response, System.currentTimeMillis())))))
			.uri("http://localhost:8080/billing-service")
	).build();
}
2020-10-09 09:45:15.180 ERROR 94422 --- [ctor-http-nio-4] a.w.r.e.AbstractErrorWebExceptionHandler : [8add6a40-1]  500 Server Error for HTTP POST "/billing-service"

org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/xml;charset=UTF-8' not supported for bodyType=com.example.gatewayexample.ServiceResponse
	at org.springframework.web.reactive.function.BodyInserters.unsupportedError(BodyInserters.java:391) ~[spring-webflux-5.2.9.RELEASE.jar:5.2.9.RELEASE]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	|_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
	|_ checkpoint ⇢ HTTP POST "/billing-service" [ExceptionHandlingWebHandler]
Stack trace:
		at org.springframework.web.reactive.function.BodyInserters.unsupportedError(BodyInserters.java:391) ~[spring-webflux-5.2.9.RELEASE.jar:5.2.9.RELEASE]
		at org.springframework.web.reactive.function.BodyInserters.lambda$writeWithMessageWriters$11(BodyInserters.java:381) ~[spring-webflux-5.2.9.RELEASE.jar:5.2.9.RELEASE]
		at java.base/java.util.Optional.orElseGet(Optional.java:369) ~[na:na]
		at org.springframework.web.reactive.function.BodyInserters.writeWithMessageWriters(BodyInserters.java:381) ~[spring-webflux-5.2.9.RELEASE.jar:5.2.9.RELEASE]
		at org.springframework.web.reactive.function.BodyInserters.lambda$fromPublisher$4(BodyInserters.java:185) ~[spring-webflux-5.2.9.RELEASE.jar:5.2.9.RELEASE]
		at org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory$ModifiedServerHttpResponse.writeWith(ModifyResponseBodyGatewayFilterFactory.java:276) ~[spring-cloud-gateway-core-2.2.5.RELEASE.jar:2.2.5.RELEASE]
		at org.springframework.cloud.gateway.filter.NettyWriteResponseFilter.lambda$filter$2(NettyWriteResponseFilter.java:100) ~[spring-cloud-gateway-core-2.2.5.RELEASE.jar:2.2.5.RELEASE]
		at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:153) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.ignoreDone(MonoIgnoreThen.java:190) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreInner.onComplete(MonoIgnoreThen.java:240) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onComplete(MonoPeekTerminal.java:292) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.Operators$MonoSubscriber.onComplete(Operators.java:1824) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.MonoIgnoreThen$ThenAcceptInner.onComplete(MonoIgnoreThen.java:314) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.Operators.complete(Operators.java:135) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.MonoEmpty.subscribe(MonoEmpty.java:45) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:153) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.ignoreDone(MonoIgnoreThen.java:190) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreInner.onComplete(MonoIgnoreThen.java:240) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2346) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribeInner(MonoFlatMapMany.java:143) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:182) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.SerializedSubscriber.onNext(SerializedSubscriber.java:99) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.FluxRetryWhen$RetryWhenMainSubscriber.onNext(FluxRetryWhen.java:162) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.core.publisher.MonoCreate$DefaultMonoSink.success(MonoCreate.java:156) ~[reactor-core-3.3.10.RELEASE.jar:3.3.10.RELEASE]
		at reactor.netty.http.client.HttpClientConnect$HttpIOHandlerObserver.onStateChange(HttpClientConnect.java:436) ~[reactor-netty-0.9.12.RELEASE.jar:0.9.12.RELEASE]
		at reactor.netty.ReactorNetty$CompositeConnectionObserver.onStateChange(ReactorNetty.java:518) ~[reactor-netty-0.9.12.RELEASE.jar:0.9.12.RELEASE]
		at reactor.netty.resources.PooledConnectionProvider$DisposableAcquire.onStateChange(PooledConnectionProvider.java:561) ~[reactor-netty-0.9.12.RELEASE.jar:0.9.12.RELEASE]
		at reactor.netty.resources.PooledConnectionProvider$PooledConnection.onStateChange(PooledConnectionProvider.java:448) ~[reactor-netty-0.9.12.RELEASE.jar:0.9.12.RELEASE]
		at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:604) ~[reactor-netty-0.9.12.RELEASE.jar:0.9.12.RELEASE]
		at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:96) ~[reactor-netty-0.9.12.RELEASE.jar:0.9.12.RELEASE]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324) ~[netty-codec-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:311) ~[netty-codec-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:425) ~[netty-codec-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276) ~[netty-codec-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) ~[netty-common-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.52.Final.jar:4.1.52.Final]
		at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.52.Final.jar:4.1.52.Final]
		at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
@ghost ghost added the waiting-for-triage label Oct 9, 2020
@spencergibb
Copy link
Member

It's an issue with serialization. Spring doesn't know how to turn om.example.gatewayexample.ServiceResponse into json. Do you have Jackson on your classpath?

@spring-cloud-issues
Copy link

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

@spring-cloud-issues
Copy link

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants