Browse Source

Support defaultHtmlEscape in WebFlux

See gh-36400

Signed-off-by: husseinvr97 <husseinmustafabas05@gmail.com>
pull/36448/head
husseinvr97 3 weeks ago committed by rstoyanchev
parent
commit
d4893fb32f
  1. 25
      spring-test/src/main/java/org/springframework/mock/web/server/MockServerWebExchange.java
  2. 9
      spring-web/src/main/java/org/springframework/web/server/ServerWebExchange.java
  3. 5
      spring-web/src/main/java/org/springframework/web/server/ServerWebExchangeDecorator.java
  4. 19
      spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java
  5. 24
      spring-web/src/main/java/org/springframework/web/server/adapter/HttpWebHandlerAdapter.java
  6. 26
      spring-web/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerBuilder.java
  7. 42
      spring-web/src/test/java/org/springframework/web/server/adapter/WebHttpHandlerBuilderTests.java
  8. 18
      spring-web/src/testFixtures/java/org/springframework/web/testfixture/server/MockServerWebExchange.java
  9. 5
      spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequestBuilder.java
  10. 12
      spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RequestContext.java
  11. 41
      spring-webflux/src/test/java/org/springframework/web/reactive/result/view/RequestContextTests.java

25
spring-test/src/main/java/org/springframework/mock/web/server/MockServerWebExchange.java

@ -49,10 +49,17 @@ public final class MockServerWebExchange extends DefaultServerWebExchange { @@ -49,10 +49,17 @@ public final class MockServerWebExchange extends DefaultServerWebExchange {
MockServerHttpRequest request, @Nullable WebSessionManager sessionManager,
@Nullable ApplicationContext applicationContext, @Nullable Principal principal) {
this(request, sessionManager, applicationContext, null, principal);
}
private MockServerWebExchange(
MockServerHttpRequest request, @Nullable WebSessionManager sessionManager,
@Nullable ApplicationContext applicationContext, @Nullable Boolean defaultHtmlEscape, @Nullable Principal principal) {
super(request, new MockServerHttpResponse(),
sessionManager != null ? sessionManager : new DefaultWebSessionManager(),
ServerCodecConfigurer.create(), new AcceptHeaderLocaleContextResolver(),
applicationContext);
applicationContext, defaultHtmlEscape);
this.principalMono = (principal != null) ? Mono.just(principal) : Mono.empty();
}
@ -125,6 +132,8 @@ public final class MockServerWebExchange extends DefaultServerWebExchange { @@ -125,6 +132,8 @@ public final class MockServerWebExchange extends DefaultServerWebExchange {
private @Nullable ApplicationContext applicationContext;
private @Nullable Boolean defaultHtmlEscape;
private @Nullable Principal principal;
public Builder(MockServerHttpRequest request) {
@ -163,6 +172,18 @@ public final class MockServerWebExchange extends DefaultServerWebExchange { @@ -163,6 +172,18 @@ public final class MockServerWebExchange extends DefaultServerWebExchange {
return this;
}
/**
* Set the default HTML escape setting for the exchange.
* @param defaultHtmlEscape whether to enable default HTML escaping,
* or {@code null} if not configured
* @return this builder
* @since 7.0.6
*/
public Builder defaultHtmlEscape(@Nullable Boolean defaultHtmlEscape) {
this.defaultHtmlEscape = defaultHtmlEscape;
return this;
}
/**
* Provide a user to associate with the exchange.
* @param principal the principal to use
@ -178,7 +199,7 @@ public final class MockServerWebExchange extends DefaultServerWebExchange { @@ -178,7 +199,7 @@ public final class MockServerWebExchange extends DefaultServerWebExchange {
*/
public MockServerWebExchange build() {
return new MockServerWebExchange(
this.request, this.sessionManager, this.applicationContext, this.principal);
this.request, this.sessionManager, this.applicationContext, this.defaultHtmlEscape, this.principal);
}
}

9
spring-web/src/main/java/org/springframework/web/server/ServerWebExchange.java

@ -169,6 +169,15 @@ public interface ServerWebExchange { @@ -169,6 +169,15 @@ 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.

5
spring-web/src/main/java/org/springframework/web/server/ServerWebExchangeDecorator.java

@ -98,6 +98,11 @@ public class ServerWebExchangeDecorator implements ServerWebExchange { @@ -98,6 +98,11 @@ 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();

19
spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java

@ -101,6 +101,8 @@ public class DefaultServerWebExchange implements ServerWebExchange { @@ -101,6 +101,8 @@ 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;
@ -114,13 +116,20 @@ public class DefaultServerWebExchange implements ServerWebExchange { @@ -114,13 +116,20 @@ public class DefaultServerWebExchange implements ServerWebExchange {
WebSessionManager sessionManager, ServerCodecConfigurer codecConfigurer,
LocaleContextResolver localeContextResolver) {
this(request, response, sessionManager, codecConfigurer, localeContextResolver, null);
this(request, response, sessionManager, codecConfigurer, localeContextResolver, null, null);
}
protected DefaultServerWebExchange(ServerHttpRequest request, ServerHttpResponse response,
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");
@ -137,6 +146,7 @@ public class DefaultServerWebExchange implements ServerWebExchange { @@ -137,6 +146,7 @@ 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);
@ -278,6 +288,11 @@ public class DefaultServerWebExchange implements ServerWebExchange { @@ -278,6 +288,11 @@ public class DefaultServerWebExchange implements ServerWebExchange {
return this.applicationContext;
}
@Override
public @Nullable Boolean getDefaultHtmlEscape() {
return this.defaultHtmlEscape;
}
@Override
public boolean isNotModified() {
return this.notModified;

24
spring-web/src/main/java/org/springframework/web/server/adapter/HttpWebHandlerAdapter.java

@ -99,6 +99,8 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa @@ -99,6 +99,8 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
private @Nullable ApplicationContext applicationContext;
private @Nullable Boolean defaultHtmlEscape;
/** Whether to log potentially sensitive info (form data at DEBUG, headers at TRACE). */
private boolean enableLoggingRequestDetails = false;
@ -250,6 +252,26 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa @@ -250,6 +252,26 @@ 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
* complete initialization.
@ -300,7 +322,7 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa @@ -300,7 +322,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);
getCodecConfigurer(), getLocaleContextResolver(), this.applicationContext, this.defaultHtmlEscape);
}
/**

26
spring-web/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerBuilder.java

@ -91,6 +91,8 @@ public final class WebHttpHandlerBuilder { @@ -91,6 +91,8 @@ 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;
@ -130,6 +132,7 @@ public final class WebHttpHandlerBuilder { @@ -130,6 +132,7 @@ public final class WebHttpHandlerBuilder {
this.observationRegistry = other.observationRegistry;
this.observationConvention = other.observationConvention;
this.httpHandlerDecorator = other.httpHandlerDecorator;
this.defaultHtmlEscape = other.defaultHtmlEscape;
}
@ -289,6 +292,26 @@ public final class WebHttpHandlerBuilder { @@ -289,6 +292,26 @@ 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
@ -424,6 +447,9 @@ public final class WebHttpHandlerBuilder { @@ -424,6 +447,9 @@ public final class WebHttpHandlerBuilder {
if (this.applicationContext != null) {
adapted.setApplicationContext(this.applicationContext);
}
if(this.defaultHtmlEscape != null) {
adapted.setDefaultHtmlEscape(this.defaultHtmlEscape);
}
adapted.afterPropertiesSet();
return (this.httpHandlerDecorator != null ? this.httpHandlerDecorator.apply(adapted) : adapted);

42
spring-web/src/test/java/org/springframework/web/server/adapter/WebHttpHandlerBuilderTests.java

@ -127,6 +127,48 @@ class WebHttpHandlerBuilderTests { @@ -127,6 +127,48 @@ class WebHttpHandlerBuilderTests {
assertThat(((HttpWebHandlerAdapter) builder.clone().build()).getApplicationContext()).isSameAs(context);
}
@Test
void defaultHtmlEscape() {
HttpHandler httpHandler = WebHttpHandlerBuilder
.webHandler(exchange -> Mono.empty())
.defaultHtmlEscape(true)
.build();
assertThat(httpHandler).isInstanceOf(HttpWebHandlerAdapter.class);
assertThat(((HttpWebHandlerAdapter) httpHandler).getDefaultHtmlEscape()).isTrue();
}
@Test
void defaultHtmlEscapeSetToFalse() {
HttpHandler httpHandler = WebHttpHandlerBuilder
.webHandler(exchange -> Mono.empty())
.defaultHtmlEscape(false)
.build();
assertThat(httpHandler).isInstanceOf(HttpWebHandlerAdapter.class);
assertThat(((HttpWebHandlerAdapter) httpHandler).getDefaultHtmlEscape()).isFalse();
}
@Test
void defaultHtmlEscapeNotConfigured() {
HttpHandler httpHandler = WebHttpHandlerBuilder
.webHandler(exchange -> Mono.empty())
.build();
assertThat(httpHandler).isInstanceOf(HttpWebHandlerAdapter.class);
assertThat(((HttpWebHandlerAdapter) httpHandler).getDefaultHtmlEscape()).isNull();
}
@Test
void cloneWithDefaultHtmlEscape() {
WebHttpHandlerBuilder builder = WebHttpHandlerBuilder
.webHandler(exchange -> Mono.empty())
.defaultHtmlEscape(true);
assertThat(((HttpWebHandlerAdapter) builder.build()).getDefaultHtmlEscape()).isTrue();
assertThat(((HttpWebHandlerAdapter) builder.clone().build()).getDefaultHtmlEscape()).isTrue();
}
@Test
void httpHandlerDecorator() {
BiFunction<ServerHttpRequest, String, ServerHttpRequest> mutator =

18
spring-web/src/testFixtures/java/org/springframework/web/testfixture/server/MockServerWebExchange.java

@ -40,9 +40,9 @@ import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRe @@ -40,9 +40,9 @@ import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRe
public final class MockServerWebExchange extends DefaultServerWebExchange {
private MockServerWebExchange(MockServerHttpRequest request, WebSessionManager sessionManager) {
private MockServerWebExchange(MockServerHttpRequest request, WebSessionManager sessionManager, Boolean defaultHtmlEscape) {
super(request, new MockServerHttpResponse(), sessionManager,
ServerCodecConfigurer.create(), new AcceptHeaderLocaleContextResolver());
ServerCodecConfigurer.create(), new AcceptHeaderLocaleContextResolver(), null, defaultHtmlEscape);
}
@ -101,6 +101,8 @@ public final class MockServerWebExchange extends DefaultServerWebExchange { @@ -101,6 +101,8 @@ public final class MockServerWebExchange extends DefaultServerWebExchange {
private @Nullable WebSessionManager sessionManager;
private @Nullable Boolean defaultHtmlEscape;
public Builder(MockServerHttpRequest request) {
this.request = request;
@ -127,12 +129,22 @@ public final class MockServerWebExchange extends DefaultServerWebExchange { @@ -127,12 +129,22 @@ public final class MockServerWebExchange extends DefaultServerWebExchange {
return this;
}
/**
* Configure the default HTML escaping setting for the exchange.
* @param defaultHtmlEscape the default HTML escaping setting to use
* @since 7.0.6
*/
public Builder defaultHtmlEscape(boolean defaultHtmlEscape) {
this.defaultHtmlEscape = defaultHtmlEscape;
return this;
}
/**
* Build the {@code MockServerWebExchange} instance.
*/
public MockServerWebExchange build() {
return new MockServerWebExchange(this.request,
this.sessionManager != null ? this.sessionManager : new DefaultWebSessionManager());
this.sessionManager != null ? this.sessionManager : new DefaultWebSessionManager(), this.defaultHtmlEscape);
}
}

5
spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequestBuilder.java

@ -434,6 +434,11 @@ class DefaultServerRequestBuilder implements ServerRequest.Builder { @@ -434,6 +434,11 @@ class DefaultServerRequestBuilder implements ServerRequest.Builder {
return this.delegate.getApplicationContext();
}
@Override
public @Nullable Boolean getDefaultHtmlEscape() {
return this.delegate.getDefaultHtmlEscape();
}
@Override
public boolean isNotModified() {
return this.delegate.isNotModified();

12
spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RequestContext.java

@ -94,7 +94,7 @@ public class RequestContext { @@ -94,7 +94,7 @@ public class RequestContext {
tzaLocaleContext.getTimeZone() : null);
this.timeZone = (timeZone != null ? timeZone : TimeZone.getDefault());
this.defaultHtmlEscape = null; // TODO
this.defaultHtmlEscape = exchange.getDefaultHtmlEscape() != null ? exchange.getDefaultHtmlEscape() : false;
this.dataValueProcessor = dataValueProcessor;
}
@ -150,7 +150,6 @@ public class RequestContext { @@ -150,7 +150,6 @@ public class RequestContext {
/**
* (De)activate default HTML escaping for messages and errors, for the scope
* of this RequestContext.
* <p>TODO: currently no application-wide setting ...
*/
public void setDefaultHtmlEscape(boolean defaultHtmlEscape) {
this.defaultHtmlEscape = defaultHtmlEscape;
@ -165,10 +164,11 @@ public class RequestContext { @@ -165,10 +164,11 @@ public class RequestContext {
}
/**
* Return the default HTML escape setting, differentiating between no default
* specified and an explicit value.
* @return whether default HTML escaping is enabled (null = no explicit default)
*/
* Return the default HTML escape setting, differentiating between no default
* specified and an explicit value.
* @return whether default HTML escaping is enabled (null = no explicit default
* specified at the handler level or the request context level)
*/
public @Nullable Boolean getDefaultHtmlEscape() {
return this.defaultHtmlEscape;
}

41
spring-webflux/src/test/java/org/springframework/web/reactive/result/view/RequestContextTests.java

@ -73,4 +73,45 @@ class RequestContextTests { @@ -73,4 +73,45 @@ class RequestContextTests {
assertThat(context.getContextUrl("{foo}?spam={spam}", map)).isEqualTo("/foo/bar%20baz?spam=%26bucket%3D");
}
@Test
void defaultHtmlEscapeNotConfigured() {
RequestContext context = new RequestContext(this.exchange, this.model, this.applicationContext);
assertThat(context.getDefaultHtmlEscape()).isFalse();
assertThat(context.isDefaultHtmlEscape()).isFalse();
}
@Test
void defaultHtmlEscapeSetToTrue() {
MockServerWebExchange exchange = MockServerWebExchange.builder(
MockServerHttpRequest.get("/foo/path").contextPath("/foo"))
.defaultHtmlEscape(true)
.build();
RequestContext context = new RequestContext(exchange, this.model, this.applicationContext);
assertThat(context.getDefaultHtmlEscape()).isTrue();
assertThat(context.isDefaultHtmlEscape()).isTrue();
}
@Test
void defaultHtmlEscapeSetToFalse() {
MockServerWebExchange exchange = MockServerWebExchange.builder(
MockServerHttpRequest.get("/foo/path").contextPath("/foo"))
.defaultHtmlEscape(false)
.build();
RequestContext context = new RequestContext(exchange, this.model, this.applicationContext);
assertThat(context.getDefaultHtmlEscape()).isFalse();
assertThat(context.isDefaultHtmlEscape()).isFalse();
}
@Test
void defaultHtmlEscapeOverriddenPerRequest() {
MockServerWebExchange exchange = MockServerWebExchange.builder(
MockServerHttpRequest.get("/foo/path").contextPath("/foo"))
.defaultHtmlEscape(true)
.build();
RequestContext context = new RequestContext(exchange, this.model, this.applicationContext);
context.setDefaultHtmlEscape(false);
assertThat(context.getDefaultHtmlEscape()).isFalse();
assertThat(context.isDefaultHtmlEscape()).isFalse();
}
}

Loading…
Cancel
Save