From 411b355f2c47c5835329225032645f4b2ee40f9d Mon Sep 17 00:00:00 2001 From: Nheyll Date: Sun, 16 Jul 2023 11:17:53 +0200 Subject: [PATCH] Document buffering with a WebClient filter See gh-28758 --- .../web/webflux-webclient/client-filter.adoc | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/framework-docs/modules/ROOT/pages/web/webflux-webclient/client-filter.adoc b/framework-docs/modules/ROOT/pages/web/webflux-webclient/client-filter.adoc index 4fd49bf8045..006a67dfb02 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux-webclient/client-filter.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux-webclient/client-filter.adoc @@ -148,5 +148,73 @@ Kotlin:: ---- ====== +The example below demonstrates how to use the `ExchangeFilterFunction` interface to create +a custom filter class that helps with computing a `Content-Length` header for `PUT` and `POST` +`multipart/form-data` requests using buffering. +[tabs] +====== +Java:: ++ +[source,java,indent=0,subs="verbatim,quotes",role="primary"] +---- +public class MultipartExchangeFilterFunction implements ExchangeFilterFunction { + @Override + @Nonnull + public Mono filter(@Nonnull ClientRequest request, @Nonnull ExchangeFunction next) { + if (MediaType.MULTIPART_FORM_DATA.includes(request.headers().getContentType()) + && (request.method() == HttpMethod.PUT || request.method() == HttpMethod.POST)) { + return next.exchange( + ClientRequest.from(request) + .body((outputMessage, context) -> request.body().insert(new BufferingDecorator(outputMessage), context)) + .build() + ); + } else { + return next.exchange(request); + } + } + + private static final class BufferingDecorator extends ClientHttpRequestDecorator { + + private BufferingDecorator(ClientHttpRequest delegate) { + super(delegate); + } + + @Override + @Nonnull + public Mono writeWith(@Nonnull Publisher body) { + return DataBufferUtils.join(body).flatMap(buffer -> { + getHeaders().setContentLength(buffer.readableByteCount()); + return super.writeWith(Mono.just(buffer)); + }); + } + } +} +---- + +Kotlin:: ++ +[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] +---- +class MultipartExchangeFilterFunction : ExchangeFilterFunction { + + override fun filter(request: ClientRequest, next: ExchangeFunction): Mono { + return next.exchange(ClientRequest.from(request) + .body { message: ClientHttpRequest?, context: BodyInserter.Context? -> request.body().insert(BufferingDecorator(message), context!!) } + .build()) + } + + private class BufferingDecorator(delegate: ClientHttpRequest?) : ClientHttpRequestDecorator(delegate!!) { + override fun writeWith(body: Publisher): Mono { + return DataBufferUtils.join(body) + .flatMap { dataBuffer: DataBuffer -> + val length = dataBuffer.readableByteCount() + headers.contentLength = length.toLong() + super.writeWith(Mono.just(dataBuffer)) + } + } + } +} +---- +====== \ No newline at end of file