Do not override requestContextAttribute with null in UrlBasedViewResolvers

Prior to this commit, if a subclass of
org.springframework.web.servlet.view.AbstractView or
org.springframework.web.reactive.result.view.AbstractUrlBasedView
configured a custom value for the requestContextAttribute, that value
was overwritten with null whenever the View was dynamically
instantiated by a UrlBasedViewResolver, and this could lead to
confusing behavior for users of the View.

This commit addresses this issue by ensuring that the
UrlBasedViewResolvers in spring-webmvc and spring-webflux do not
override the requestContextAttribute in a View if the
UrlBasedViewResolver has not been explicitly configured with a custom
requestContextAttribute value.

Closes gh-23129
This commit is contained in:
Sam Brannen
2019-06-13 17:42:14 +03:00
parent c4075cf216
commit 451cce4fe8
4 changed files with 234 additions and 166 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,8 +30,8 @@ import org.springframework.util.Assert;
import org.springframework.util.PatternMatchUtils;
/**
* A {@link ViewResolver} that allow direct resolution of symbolic view names
* to URLs without explicit mapping definition. This is useful if symbolic names
* A {@link ViewResolver} that allows direct resolution of symbolic view names
* to URLs without explicit mapping definitions. This is useful if symbolic names
* match the names of view resources in a straightforward manner (i.e. the
* symbolic name is the unique part of the resource's filename), without the need
* for a dedicated mapping to be defined for each view.
@@ -59,6 +59,7 @@ import org.springframework.util.PatternMatchUtils;
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @author Juergen Hoeller
* @author Sam Brannen
* @since 5.0
*/
public class UrlBasedViewResolver extends ViewResolverSupport
@@ -266,7 +267,7 @@ public class UrlBasedViewResolver extends ViewResolverSupport
/**
* Creates a new View instance of the specified view class and configures it.
* Does <i>not</i> perform any lookup for pre-defined View instances.
* <p>Does <i>not</i> perform any lookup for pre-defined View instances.
* <p>Spring lifecycle methods as defined by the bean container do not have to
* be called here: They will be automatically applied afterwards, provided
* that an {@link #setApplicationContext ApplicationContext} is available.
@@ -281,10 +282,14 @@ public class UrlBasedViewResolver extends ViewResolverSupport
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
view.setSupportedMediaTypes(getSupportedMediaTypes());
view.setRequestContextAttribute(getRequestContextAttribute());
view.setDefaultCharset(getDefaultCharset());
view.setUrl(getPrefix() + viewName + getSuffix());
String requestContextAttribute = getRequestContextAttribute();
if (requestContextAttribute != null) {
view.setRequestContextAttribute(requestContextAttribute);
}
return view;
}

View File

@@ -37,20 +37,60 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @author Sam Brannen
*/
public class UrlBasedViewResolverTests {
private UrlBasedViewResolver resolver;
private final UrlBasedViewResolver resolver = new UrlBasedViewResolver();
@Before
public void setup() {
StaticApplicationContext context = new StaticApplicationContext();
context.refresh();
this.resolver = new UrlBasedViewResolver();
this.resolver.setApplicationContext(context);
}
@Test
public void urlBasedViewResolverOverridesCustomRequestContextAttributeWithNonNullValue() throws Exception {
assertThat(new TestView().getRequestContextAttribute())
.as("requestContextAttribute when instantiated directly")
.isEqualTo("testRequestContext");
this.resolver.setViewClass(TestView.class);
this.resolver.setRequestContextAttribute("viewResolverRequestContext");
Mono<View> mono = this.resolver.resolveViewName("example", Locale.getDefault());
StepVerifier.create(mono)
.consumeNextWith(view -> {
assertThat(view).isInstanceOf(TestView.class);
assertThat(((TestView) view).getRequestContextAttribute())
.as("requestContextAttribute when instantiated dynamically by UrlBasedViewResolver")
.isEqualTo("viewResolverRequestContext");
})
.expectComplete()
.verify(Duration.ZERO);
}
@Test
public void urlBasedViewResolverDoesNotOverrideCustomRequestContextAttributeWithNull() throws Exception {
assertThat(new TestView().getRequestContextAttribute())
.as("requestContextAttribute when instantiated directly")
.isEqualTo("testRequestContext");
this.resolver.setViewClass(TestView.class);
Mono<View> mono = this.resolver.resolveViewName("example", Locale.getDefault());
StepVerifier.create(mono)
.consumeNextWith(view -> {
assertThat(view).isInstanceOf(TestView.class);
assertThat(((TestView) view).getRequestContextAttribute())
.as("requestContextAttribute when instantiated dynamically by UrlBasedViewResolver")
.isEqualTo("testRequestContext");
})
.expectComplete()
.verify(Duration.ZERO);
}
@Test
public void viewNames() throws Exception {
@@ -98,6 +138,10 @@ public class UrlBasedViewResolverTests {
private static class TestView extends AbstractUrlBasedView {
public TestView() {
setRequestContextAttribute("testRequestContext");
}
@Override
public boolean checkResourceExists(Locale locale) throws Exception {
return true;