diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/filters.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/filters.adoc index d2813753a95..490f5d9e623 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc/filters.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc/filters.adoc @@ -3,7 +3,10 @@ [.small]#xref:web/webflux/reactive-spring.adoc#webflux-filters[See equivalent in the Reactive stack]# -The `spring-web` module provides some useful filters: +In the Servlet API, you can add a `jakarta.servlet.Filter` to apply interception-style logic +before and after the rest of the processing chain of filters and the target `Servlet`. + +The `spring-web` module has a number of built-in `Filter` implementations: * xref:web/webmvc/filters.adoc#filters-http-put[Form Data] * xref:web/webmvc/filters.adoc#filters-forwarded-headers[Forwarded Headers] @@ -11,9 +14,19 @@ The `spring-web` module provides some useful filters: * xref:web/webmvc/filters.adoc#filters-cors[CORS] * xref:web/webmvc/filters.adoc#filters.url-handler[URL Handler] -Servlet filters can be configured in the `web.xml` configuration file or using Servlet annotations. -If you are using Spring Boot, you can -{spring-boot-docs}/how-to/webserver.html#howto.webserver.add-servlet-filter-listener.spring-bean[declare them as beans and configure them as part of your application]. +There are also base class implementations for use in Spring applications: + +* `GenericFilterBean` -- base class for a `Filter` configured as a Spring bean; +integrates with the Spring `ApplicationContext` lifecycle. +* `OncePerRequestFilter` -- extension of `GenericFilterBean` that supports a single +invocation at the start of a request, i.e. during the `REQUEST` dispatch phase, and +ignoring further handling via `FORWARD` dispatches. The filter also provides control +over whether the `Filter` gets involved in `ASYNC` and `ERROR` dispatches. + +Servlet filters can be configured in `web.xml` or via Servlet annotations. +In a Spring Boot application , you can +{spring-boot-docs}/how-to/webserver.html#howto.webserver.add-servlet-filter-listener.spring-bean[declare Filter's as beans] +and Boot will have them configured. [[filters-http-put]] diff --git a/spring-web/src/main/java/org/springframework/http/StreamingHttpOutputMessage.java b/spring-web/src/main/java/org/springframework/http/StreamingHttpOutputMessage.java index fd77c83f0e9..6a48417728c 100644 --- a/spring-web/src/main/java/org/springframework/http/StreamingHttpOutputMessage.java +++ b/spring-web/src/main/java/org/springframework/http/StreamingHttpOutputMessage.java @@ -22,10 +22,16 @@ import java.io.OutputStream; import org.springframework.util.StreamUtils; /** - * Represents an HTTP output message that allows for setting a streaming body. - * Note that such messages typically do not support {@link #getBody()} access. + * Contract for {@code HttpOutputMessage} implementations to expose the ability + * to stream request body content by writing to an {@link OutputStream} from + * a callback. + * + *
The {@link #setBody(Body)} method provides the option to stream, and is + * mutually exclusive use of {@link #getBody()}, which instead returns an + * {@code OutputStream} that aggregates the request body before sending it. * * @author Arjen Poutsma + * @author Rossen Stoyanchev * @since 4.0 * @see #setBody */ @@ -33,6 +39,8 @@ public interface StreamingHttpOutputMessage extends HttpOutputMessage { /** * Set the streaming body callback for this message. + *
Note that this is mutually exclusive with {@link #getBody()}, which + * may instead aggregate the request body before sending it. * @param body the streaming body callback */ void setBody(Body body); @@ -60,9 +68,9 @@ public interface StreamingHttpOutputMessage extends HttpOutputMessage { /** - * Defines the contract for bodies that can be written directly to an - * {@link OutputStream}. Useful with HTTP client libraries that provide - * indirect access to an {@link OutputStream} via a callback mechanism. + * Contract to stream request body content to an {@link OutputStream}. + * In some HTTP client libraries this is only possible indirectly through a + * callback mechanism. */ @FunctionalInterface interface Body { diff --git a/spring-web/src/main/java/org/springframework/http/client/AbstractStreamingClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/AbstractStreamingClientHttpRequest.java index d722f6e0719..529c0058635 100644 --- a/spring-web/src/main/java/org/springframework/http/client/AbstractStreamingClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/AbstractStreamingClientHttpRequest.java @@ -22,16 +22,23 @@ import java.io.OutputStream; import org.jspecify.annotations.Nullable; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpOutputMessage; import org.springframework.http.StreamingHttpOutputMessage; import org.springframework.util.Assert; import org.springframework.util.FastByteArrayOutputStream; /** - * Abstract base for {@link ClientHttpRequest} that also implement - * {@link StreamingHttpOutputMessage}. Ensures that headers and - * body are not written multiple times. + * Extension of {@link AbstractClientHttpRequest} that adds the ability to stream + * request body content directly to the underlying HTTP client library through + * the {@link StreamingHttpOutputMessage} contract. + * + *
It is necessary to call {@link #setBody} and stream the request body through + * a callback for access to the {@code OutputStream}. The alternative to call + * {@link #getBody()} is also supported as a fallback, but that does not stream, + * and returns an aggregating {@code OutputStream} instead. * * @author Arjen Poutsma + * @author Rossen Stoyanchev * @since 6.1 */ abstract class AbstractStreamingClientHttpRequest extends AbstractClientHttpRequest @@ -42,6 +49,12 @@ abstract class AbstractStreamingClientHttpRequest extends AbstractClientHttpRequ private @Nullable FastByteArrayOutputStream bodyStream; + /** + * Implements the {@link HttpOutputMessage} contract for request body content. + *
Note that this method does not result in streaming, and the returned + * {@code OutputStream} aggregates the full content in a byte[] before + * sending. To use streaming, call {@link #setBody} instead. + */ @Override protected final OutputStream getBodyInternal(HttpHeaders headers) { Assert.state(this.body == null, "Invoke either getBody or setBody; not both"); @@ -52,6 +65,10 @@ abstract class AbstractStreamingClientHttpRequest extends AbstractClientHttpRequ return this.bodyStream; } + /** + * Implements the {@link StreamingHttpOutputMessage} contract for writing + * request body by streaming directly to the underlying HTTP client. + */ @Override public final void setBody(Body body) { Assert.notNull(body, "Body must not be null"); @@ -72,12 +89,14 @@ abstract class AbstractStreamingClientHttpRequest extends AbstractClientHttpRequ /** - * Abstract template method that writes the given headers and content to the HTTP request. - * @param headers the HTTP headers + * Abstract method for concrete implementations to write the headers and + * {@link StreamingHttpOutputMessage.Body} to the HTTP request. + * @param headers the HTTP headers for the request * @param body the HTTP body, may be {@code null} if no body was {@linkplain #setBody(Body) set} * @return the response object for the executed request * @since 6.1 */ - protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, @Nullable Body body) throws IOException; + protected abstract ClientHttpResponse executeInternal( + HttpHeaders headers, @Nullable Body body) throws IOException; } diff --git a/spring-web/src/main/java/org/springframework/web/client/RequestCallback.java b/spring-web/src/main/java/org/springframework/web/client/RequestCallback.java index ace09c94f97..22b2b4c189f 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RequestCallback.java +++ b/spring-web/src/main/java/org/springframework/web/client/RequestCallback.java @@ -19,6 +19,7 @@ package org.springframework.web.client; import java.io.IOException; import java.lang.reflect.Type; +import org.springframework.http.HttpOutputMessage; import org.springframework.http.client.ClientHttpRequest; /** @@ -44,6 +45,12 @@ public interface RequestCallback { * Gets called by {@link RestTemplate#execute} with an opened {@code ClientHttpRequest}. * Does not need to care about closing the request or about handling errors: * this will all be handled by the {@code RestTemplate}. + *
Note: In order to stream request body content directly + * to the underlying HTTP library, implementations must check if the request + * is an implementation of {@link org.springframework.http.StreamingHttpOutputMessage}, + * and set the request body through it. Use of the {@link HttpOutputMessage#getBody()} + * is also supported, but results in full content aggregation prior to execution. + * All built-in request implementations support {@code StreamingHttpOutputMessage}. * @param request the active HTTP request * @throws IOException in case of I/O errors */