Polishing contribution

Closes gh-36400
This commit is contained in:
rstoyanchev
2026-03-10 19:14:41 +00:00
parent d4893fb32f
commit 5168e3a38b
11 changed files with 122 additions and 195 deletions
@@ -43,6 +43,12 @@ import org.springframework.util.MultiValueMap;
*/
public interface ServerWebExchange {
/**
* HTML escape attribute, populated from the value of
* {@link org.springframework.web.server.adapter.WebHttpHandlerBuilder#defaultHtmlEscape(Boolean)}.
*/
String HTML_ESCAPE_ATTRIBUTE = ServerWebExchange.class.getName() + ".HTML_ESCAPE";
/**
* Name of {@link #getAttributes() attribute} whose value can be used to
* correlate log messages for this exchange. Use {@link #getLogPrefix()} to
@@ -169,15 +175,6 @@ public interface ServerWebExchange {
*/
@Nullable ApplicationContext getApplicationContext();
/**
* Return the default HTML escape setting available for the current request,
* or {@code null} if no default was configured at the handler level.
* @return whether default HTML escaping is enabled, or {@code null} if not configured
* @since 7.0.6
* @see org.springframework.web.server.adapter.WebHttpHandlerBuilder#defaultHtmlEscape(boolean)
*/
@Nullable Boolean getDefaultHtmlEscape();
/**
* Returns {@code true} if the one of the {@code checkNotModified} methods
* in this contract were used and they returned true.
@@ -98,11 +98,6 @@ public class ServerWebExchangeDecorator implements ServerWebExchange {
return getDelegate().getApplicationContext();
}
@Override
public @Nullable Boolean getDefaultHtmlEscape() {
return getDelegate().getDefaultHtmlEscape();
}
@Override
public Mono<MultiValueMap<String, String>> getFormData() {
return getDelegate().getFormData();
@@ -101,8 +101,6 @@ public class DefaultServerWebExchange implements ServerWebExchange {
private final @Nullable ApplicationContext applicationContext;
private final @Nullable Boolean defaultHtmlEscape;
private volatile boolean notModified;
private Function<String, String> urlTransformer = url -> url;
@@ -116,20 +114,13 @@ public class DefaultServerWebExchange implements ServerWebExchange {
WebSessionManager sessionManager, ServerCodecConfigurer codecConfigurer,
LocaleContextResolver localeContextResolver) {
this(request, response, sessionManager, codecConfigurer, localeContextResolver, null, null);
this(request, response, sessionManager, codecConfigurer, localeContextResolver, null);
}
public DefaultServerWebExchange(ServerHttpRequest request, ServerHttpResponse response,
WebSessionManager sessionManager, ServerCodecConfigurer codecConfigurer,
LocaleContextResolver localeContextResolver, @Nullable ApplicationContext applicationContext) {
this(request, response, sessionManager, codecConfigurer, localeContextResolver, applicationContext, null);
}
protected DefaultServerWebExchange(ServerHttpRequest request, ServerHttpResponse response,
WebSessionManager sessionManager, ServerCodecConfigurer codecConfigurer,
LocaleContextResolver localeContextResolver, @Nullable ApplicationContext applicationContext, @Nullable Boolean defaultHtmlEscape) {
Assert.notNull(request, "'request' is required");
Assert.notNull(response, "'response' is required");
Assert.notNull(sessionManager, "'sessionManager' is required");
@@ -146,7 +137,6 @@ public class DefaultServerWebExchange implements ServerWebExchange {
this.formDataMono = initFormData(request, codecConfigurer, getLogPrefix());
this.multipartDataMono = initMultipartData(codecConfigurer, getLogPrefix());
this.applicationContext = applicationContext;
this.defaultHtmlEscape = defaultHtmlEscape;
if (request instanceof AbstractServerHttpRequest abstractServerHttpRequest) {
abstractServerHttpRequest.setAttributesSupplier(() -> this.attributes);
@@ -288,11 +278,6 @@ public class DefaultServerWebExchange implements ServerWebExchange {
return this.applicationContext;
}
@Override
public @Nullable Boolean getDefaultHtmlEscape() {
return this.defaultHtmlEscape;
}
@Override
public boolean isNotModified() {
return this.notModified;
@@ -97,10 +97,10 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
private ServerRequestObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION;
private @Nullable ApplicationContext applicationContext;
private @Nullable Boolean defaultHtmlEscape;
private @Nullable ApplicationContext applicationContext;
/** Whether to log potentially sensitive info (form data at DEBUG, headers at TRACE). */
private boolean enableLoggingRequestDetails = false;
@@ -233,6 +233,26 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
return this.observationConvention;
}
/**
* Configure whether default HTML escaping is enabled for the web application.
* The setting is then exposed as the exchanger attribute
* {@link ServerWebExchange#HTML_ESCAPE_ATTRIBUTE}.
* @param defaultHtmlEscape whether to enable default HTML escaping
* @since 7.0.6
*/
public void setDefaultHtmlEscape(Boolean defaultHtmlEscape) {
this.defaultHtmlEscape = defaultHtmlEscape;
}
/**
* Return the configured default HTML escape setting,
* or {@code null} if not configured.
* @since 7.0.6
*/
public @Nullable Boolean getDefaultHtmlEscape() {
return this.defaultHtmlEscape;
}
/**
* Configure the {@code ApplicationContext} associated with the web application,
* if it was initialized with one via
@@ -252,25 +272,6 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
return this.applicationContext;
}
/**
* Configure a default HTML escape setting to apply to every
* {@link org.springframework.web.server.ServerWebExchange} created
* by this adapter.
* @param defaultHtmlEscape whether to enable default HTML escaping
* @since 7.0.6
*/
public void setDefaultHtmlEscape(Boolean defaultHtmlEscape) {
this.defaultHtmlEscape = defaultHtmlEscape;
}
/**
* Return the configured default HTML escape setting,
* or {@code null} if not configured.
* @since 7.0.6
*/
public @Nullable Boolean getDefaultHtmlEscape() {
return this.defaultHtmlEscape;
}
/**
* This method must be invoked after all properties have been set to
@@ -312,6 +313,10 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
exchange.getAttributes().put(
ServerRequestObservationContext.CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE, observationContext);
if (this.defaultHtmlEscape != null) {
exchange.getAttributes().put(ServerWebExchange.HTML_ESCAPE_ATTRIBUTE, this.defaultHtmlEscape);
}
return getDelegate().handle(exchange)
.doOnSuccess(aVoid -> logResponse(exchange))
.onErrorResume(ex -> handleUnresolvedError(exchange, observationContext, ex))
@@ -322,7 +327,7 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
protected ServerWebExchange createExchange(ServerHttpRequest request, ServerHttpResponse response) {
return new DefaultServerWebExchange(request, response, this.sessionManager,
getCodecConfigurer(), getLocaleContextResolver(), this.applicationContext, this.defaultHtmlEscape);
getCodecConfigurer(), getLocaleContextResolver(), this.applicationContext);
}
/**
@@ -91,8 +91,6 @@ public final class WebHttpHandlerBuilder {
private final List<WebExceptionHandler> exceptionHandlers = new ArrayList<>();
private @Nullable Boolean defaultHtmlEscape;
private @Nullable Function<HttpHandler, HttpHandler> httpHandlerDecorator;
private @Nullable WebSessionManager sessionManager;
@@ -107,6 +105,8 @@ public final class WebHttpHandlerBuilder {
private @Nullable ServerRequestObservationConvention observationConvention;
private @Nullable Boolean defaultHtmlEscape;
/**
* Private constructor to use when initialized from an ApplicationContext.
@@ -125,13 +125,13 @@ public final class WebHttpHandlerBuilder {
this.applicationContext = other.applicationContext;
this.filters.addAll(other.filters);
this.exceptionHandlers.addAll(other.exceptionHandlers);
this.httpHandlerDecorator = other.httpHandlerDecorator;
this.sessionManager = other.sessionManager;
this.codecConfigurer = other.codecConfigurer;
this.localeContextResolver = other.localeContextResolver;
this.forwardedHeaderTransformer = other.forwardedHeaderTransformer;
this.observationRegistry = other.observationRegistry;
this.observationConvention = other.observationConvention;
this.httpHandlerDecorator = other.httpHandlerDecorator;
this.defaultHtmlEscape = other.defaultHtmlEscape;
}
@@ -271,6 +271,31 @@ public final class WebHttpHandlerBuilder {
return this;
}
/**
* Configure a {@link Function} to decorate the {@link HttpHandler} returned
* by this builder which effectively wraps the entire
* {@link WebExceptionHandler} - {@link WebFilter} - {@link WebHandler}
* processing chain. This provides access to the request and response before
* the entire chain and likewise the ability to observe the result of
* the entire chain.
* @param handlerDecorator the decorator to apply
* @since 5.3
*/
public WebHttpHandlerBuilder httpHandlerDecorator(Function<HttpHandler, HttpHandler> handlerDecorator) {
this.httpHandlerDecorator = (this.httpHandlerDecorator != null ?
handlerDecorator.andThen(this.httpHandlerDecorator) : handlerDecorator);
return this;
}
/**
* Whether a decorator for {@link HttpHandler} is configured or not via
* {@link #httpHandlerDecorator(Function)}.
* @since 5.3
*/
public boolean hasHttpHandlerDecorator() {
return (this.httpHandlerDecorator != null);
}
/**
* Configure the {@link WebSessionManager} to set on the
* {@link ServerWebExchange WebServerExchange}.
@@ -292,26 +317,6 @@ public final class WebHttpHandlerBuilder {
return (this.sessionManager != null);
}
/**
* Configure a default HTML escape setting to apply to the created
* {@link org.springframework.web.server.ServerWebExchange}.
* @param defaultHtmlEscape whether to enable default HTML escaping
* @return this builder
* @since 7.0.6
*/
public WebHttpHandlerBuilder defaultHtmlEscape(Boolean defaultHtmlEscape) {
this.defaultHtmlEscape = defaultHtmlEscape;
return this;
}
/**
* Return whether a default HTML escape setting has been configured.
* @since 7.0.6
*/
public boolean hasDefaultHtmlEscape() {
return (this.defaultHtmlEscape != null);
}
/**
* Configure the {@link ServerCodecConfigurer} to set on the {@code WebServerExchange}.
* @param codecConfigurer the codec configurer
@@ -394,28 +399,26 @@ public final class WebHttpHandlerBuilder {
}
/**
* Configure a {@link Function} to decorate the {@link HttpHandler} returned
* by this builder which effectively wraps the entire
* {@link WebExceptionHandler} - {@link WebFilter} - {@link WebHandler}
* processing chain. This provides access to the request and response before
* the entire chain and likewise the ability to observe the result of
* the entire chain.
* @param handlerDecorator the decorator to apply
* @since 5.3
* Configure whether default HTML escaping is enabled for the web application.
* The setting is then exposed as the exchanger attribute
* {@link ServerWebExchange#HTML_ESCAPE_ATTRIBUTE}.
* <p>This method differentiates between no setting specified at all and
* an actual boolean value specified, allowing to have a context-specific
* default in case of no setting at the global level.
* @param defaultHtmlEscape whether to enable default HTML escaping
* @since 7.0.6
*/
public WebHttpHandlerBuilder httpHandlerDecorator(Function<HttpHandler, HttpHandler> handlerDecorator) {
this.httpHandlerDecorator = (this.httpHandlerDecorator != null ?
handlerDecorator.andThen(this.httpHandlerDecorator) : handlerDecorator);
public WebHttpHandlerBuilder defaultHtmlEscape(@Nullable Boolean defaultHtmlEscape) {
this.defaultHtmlEscape = defaultHtmlEscape;
return this;
}
/**
* Whether a decorator for {@link HttpHandler} is configured or not via
* {@link #httpHandlerDecorator(Function)}.
* @since 5.3
* Whether HTML escaping is enabled for the web application.
* @since 7.0.6
*/
public boolean hasHttpHandlerDecorator() {
return (this.httpHandlerDecorator != null);
public @Nullable Boolean getDefaultHtmlEscape() {
return this.defaultHtmlEscape;
}
/**
@@ -447,7 +450,7 @@ public final class WebHttpHandlerBuilder {
if (this.applicationContext != null) {
adapted.setApplicationContext(this.applicationContext);
}
if(this.defaultHtmlEscape != null) {
if (this.defaultHtmlEscape != null) {
adapted.setDefaultHtmlEscape(this.defaultHtmlEscape);
}
adapted.afterPropertiesSet();