Skip to content

Instantly share code, notes, and snippets.

@cer
Last active January 14, 2023 18:17
Show Gist options
  • Save cer/04ce15ba46f54634312740135fcfdeea to your computer and use it in GitHub Desktop.
Save cer/04ce15ba46f54634312740135fcfdeea to your computer and use it in GitHub Desktop.
Spring Webflux-based proxying controller
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Controller
public class DefaultController {
private DefaultDataBufferFactory defaultDataBufferFactory = new DefaultDataBufferFactory();
@RequestMapping(value = "/api/**", method = RequestMethod.GET)
@ResponseBody
public Mono<Void> defaultGetHandler(ServerHttpRequest serverRequest, ServerHttpResponse serverHttpResponse) {
WebClient client = WebClient.create("http://example.com");
String path = serverRequest.getPath().value();
System.out.println("Path=" + path);
// Note http://example.com/api/... returns a badly formatted content header
return client.get()
.uri("/foo/" + path)
.accept(serverRequest.getHeaders().getAccept().toArray(new MediaType[0]))
.exchange()
.flatMap(clientResponse -> {
Assert.isTrue(serverHttpResponse.setStatusCode(clientResponse.statusCode()));
serverHttpResponse.getHeaders().addAll(clientResponse.headers().asHttpHeaders());
return serverHttpResponse.writeWith(clientResponse.bodyToFlux(String.class).map(s -> defaultDataBufferFactory.wrap(s.getBytes())));
});
}
}
@ju-la-berger
Copy link

Hi there!

Your solution does not handle non-text responses correctly, e.g. when the proxied server responds with some PDF document.

I assume the following (using Kotlin, excuse me!) code snippet works better (and provides better performance probably):

.exchange()
.flatMap {
    serverHttpResponse.headers.addAll(it.headers().asHttpHeaders())
    serverHttpResponse.statusCode = it.statusCode()
    it.body { clientHttpResponse, _ -> serverHttpResponse.writeWith(clientHttpResponse.body) }
}

The idea is to use a BodyExtractor to directly access ReactiveHttpInputMessage#getBody (which returns Flux<DataBuffer>).

Best regards,
Justus

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment