Polish RedirectView
Issue: SPR-14534
This commit is contained in:
@@ -45,8 +45,9 @@ import org.springframework.web.util.UriUtils;
|
|||||||
* available in the model.
|
* available in the model.
|
||||||
*
|
*
|
||||||
* <p>A URL for this view is supposed to be a HTTP redirect which does the redirect via
|
* <p>A URL for this view is supposed to be a HTTP redirect which does the redirect via
|
||||||
* sending an {@link HttpStatus#SEE_OTHER} code. If HTTP 1.0 compatibility is needed,
|
* sending a {@literal 3xx} status code. By default {@link HttpStatus#SEE_OTHER} is used.
|
||||||
* {@link HttpStatus#FOUND} code can be set via {@link #setStatusCode(HttpStatus)}.
|
* If HTTP 1.0 compatibility is needed, {@link HttpStatus#FOUND} code can be set via
|
||||||
|
* {@link #setStatusCode(HttpStatus)}.
|
||||||
*
|
*
|
||||||
* <p>Note that the default value for the "contextRelative" flag is true.
|
* <p>Note that the default value for the "contextRelative" flag is true.
|
||||||
* With the flag on, URLs starting with "/" are considered relative to the web application
|
* With the flag on, URLs starting with "/" are considered relative to the web application
|
||||||
@@ -54,7 +55,6 @@ import org.springframework.web.util.UriUtils;
|
|||||||
* root.
|
* root.
|
||||||
*
|
*
|
||||||
* @author Sebastien Deleuze
|
* @author Sebastien Deleuze
|
||||||
* @see #setContextRelative
|
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
public class RedirectView extends AbstractUrlBasedView {
|
public class RedirectView extends AbstractUrlBasedView {
|
||||||
@@ -66,49 +66,70 @@ public class RedirectView extends AbstractUrlBasedView {
|
|||||||
|
|
||||||
private HttpStatus statusCode = HttpStatus.SEE_OTHER;
|
private HttpStatus statusCode = HttpStatus.SEE_OTHER;
|
||||||
|
|
||||||
private boolean propagateQueryParams = false;
|
private boolean propagateQuery = false;
|
||||||
|
|
||||||
private String[] hosts;
|
private String[] hosts;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@code RedirectView} with the given redirect URL.
|
* Constructor for use as a bean.
|
||||||
*
|
*/
|
||||||
* @see #builder(String)
|
public RedirectView() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@code RedirectView} with the given URL and the {@link HttpStatus#SEE_OTHER}
|
||||||
|
* status code which is the correct code for HTTP 1.1 clients.
|
||||||
*/
|
*/
|
||||||
public RedirectView(String redirectUrl) {
|
public RedirectView(String redirectUrl) {
|
||||||
super(redirectUrl);
|
super(redirectUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a builder for a {@code RedirectView}.
|
* Create a new {@code RedirectView} with the given redirect URL ans status code. Most
|
||||||
|
* frequently used ones are:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link HttpStatus#SEE_OTHER} : temporary redirect for HTTP 1.1 compatible clients</li>
|
||||||
|
* <li>{@link HttpStatus#FOUND} : temporary redirect for HTTP 1.0 compatible clients</li>
|
||||||
|
* <li>{@link HttpStatus#MOVED_PERMANENTLY} : permanent redirect</li>
|
||||||
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public static Builder builder(String redirectUrl) {
|
public RedirectView(String redirectUrl, HttpStatus statusCode) {
|
||||||
return new BuilderImpl(redirectUrl);
|
super(redirectUrl);
|
||||||
|
setStatusCode(statusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set whether to interpret a given URL that starts with a slash ("/")
|
* Set whether to interpret a given URL that starts with a slash ("/")
|
||||||
* as relative to the current context path.
|
* as relative to the current context path ({@code true}, the default) or to
|
||||||
* <p>Default is "true": the context path will be
|
* the web server root ({@code false}).
|
||||||
* prepended to the URL in such a case. If "false", an URL that starts
|
|
||||||
* with a slash will be interpreted as absolute, i.e. taken as-is.
|
|
||||||
*/
|
*/
|
||||||
public void setContextRelative(boolean contextRelative) {
|
public void setContextRelative(boolean contextRelative) {
|
||||||
this.contextRelative = contextRelative;
|
this.contextRelative = contextRelative;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a customized redirect status code to be used for a redirect. Default is
|
* Return whether to interpret a given URL that starts with a slash ("/")
|
||||||
* {@link HttpStatus#SEE_OTHER} which is the correct code for HTTP 1.1
|
* as relative to the current context path ("true") or to the web server
|
||||||
* clients. This setter can be used to configure {@link HttpStatus#FOUND}
|
* root ("false").
|
||||||
* if HTTP 1.0 clients need to be supported, or any other {@literal 3xx}
|
* @return
|
||||||
* status code.
|
*/
|
||||||
|
public boolean isContextRelative() {
|
||||||
|
return contextRelative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the redirect status code. Most frequently used ones are:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link HttpStatus#SEE_OTHER} : temporary redirect for HTTP 1.1 compatible clients</li>
|
||||||
|
* <li>{@link HttpStatus#FOUND} : temporary redirect for HTTP 1.0 compatible clients</li>
|
||||||
|
* <li>{@link HttpStatus#MOVED_PERMANENTLY} : permanent redirect</li>
|
||||||
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public void setStatusCode(HttpStatus statusCode) {
|
public void setStatusCode(HttpStatus statusCode) {
|
||||||
Assert.notNull(statusCode);
|
Assert.notNull(statusCode, "HttpStatus must not be null");
|
||||||
|
Assert.isTrue(statusCode.is3xxRedirection(), "HttpStatus must be a redirection (3xx status code)");
|
||||||
this.statusCode = statusCode;
|
this.statusCode = statusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,12 +141,19 @@ public class RedirectView extends AbstractUrlBasedView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When set to {@code true} the query string of the current URL is appended
|
* Set whether to append the query string of the current URL to the redirected URL
|
||||||
* and thus propagated through to the redirected URL.
|
* ({@code true}) or not ({@code false}, the default).
|
||||||
* <p>Defaults to {@code false}.
|
|
||||||
*/
|
*/
|
||||||
public void setPropagateQueryParams(boolean propagateQueryParams) {
|
public void setPropagateQuery(boolean propagateQuery) {
|
||||||
this.propagateQueryParams = propagateQueryParams;
|
this.propagateQuery = propagateQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether the query string of the current URL is appended to the redirected URL
|
||||||
|
* ({@code true}) or not ({@code false}).
|
||||||
|
*/
|
||||||
|
public boolean isPropagateQuery() {
|
||||||
|
return propagateQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -141,6 +169,13 @@ public class RedirectView extends AbstractUrlBasedView {
|
|||||||
this.hosts = hosts;
|
this.hosts = hosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the configured application hosts.
|
||||||
|
*/
|
||||||
|
public String[] getHosts() {
|
||||||
|
return this.hosts;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert model to request parameters and redirect to the given URL.
|
* Convert model to request parameters and redirect to the given URL.
|
||||||
* @see #sendRedirect
|
* @see #sendRedirect
|
||||||
@@ -173,7 +208,7 @@ public class RedirectView extends AbstractUrlBasedView {
|
|||||||
Map<String, String> variables = getCurrentRequestUriVariables(exchange);
|
Map<String, String> variables = getCurrentRequestUriVariables(exchange);
|
||||||
targetUrl = replaceUriTemplateVariables(targetUrl.toString(), model, variables, charset);
|
targetUrl = replaceUriTemplateVariables(targetUrl.toString(), model, variables, charset);
|
||||||
}
|
}
|
||||||
if (this.propagateQueryParams) {
|
if (this.propagateQuery) {
|
||||||
appendCurrentQueryParams(targetUrl, request);
|
appendCurrentQueryParams(targetUrl, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,74 +327,12 @@ public class RedirectView extends AbstractUrlBasedView {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Builder {
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
/**
|
super.afterPropertiesSet();
|
||||||
* @see RedirectView#setContextRelative(boolean)
|
if (getStatusCode() == null) {
|
||||||
*/
|
throw new IllegalArgumentException("Property 'statusCode' is required");
|
||||||
Builder contextRelative(boolean contextRelative);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see RedirectView#setStatusCode(HttpStatus)
|
|
||||||
*/
|
|
||||||
Builder statusCode(HttpStatus statusCode);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see RedirectView#setPropagateQueryParams(boolean)
|
|
||||||
*/
|
|
||||||
Builder propagateQueryParams(boolean propagateQueryParams);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see RedirectView#setHosts(String...)
|
|
||||||
*/
|
|
||||||
Builder hosts(String... hosts);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the redirect view.
|
|
||||||
*/
|
|
||||||
RedirectView build();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class BuilderImpl implements Builder {
|
|
||||||
|
|
||||||
private final RedirectView view;
|
|
||||||
|
|
||||||
|
|
||||||
public BuilderImpl(String redirectUrl) {
|
|
||||||
this.view = new RedirectView(redirectUrl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Builder contextRelative(boolean contextRelative) {
|
|
||||||
this.view.setContextRelative(contextRelative);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Builder statusCode(HttpStatus statusCode) {
|
|
||||||
this.view.setStatusCode(statusCode);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Builder propagateQueryParams(boolean propagateQueryParams) {
|
|
||||||
this.view.setPropagateQueryParams(propagateQueryParams);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Builder hosts(String... hosts) {
|
|
||||||
this.view.setHosts(hosts);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RedirectView build() {
|
|
||||||
return this.view;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import org.springframework.util.PatternMatchUtils;
|
|||||||
* <p>Note: This class does not support localized resolution, i.e. resolving
|
* <p>Note: This class does not support localized resolution, i.e. resolving
|
||||||
* a symbolic view name to different resources depending on the current locale.
|
* a symbolic view name to different resources depending on the current locale.
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
* @author Sebastien Deleuze
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
public class UrlBasedViewResolver extends ViewResolverSupport implements ViewResolver, InitializingBean {
|
public class UrlBasedViewResolver extends ViewResolverSupport implements ViewResolver, InitializingBean {
|
||||||
|
|||||||
@@ -76,13 +76,11 @@ public class RedirectViewTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void customStatusCode() {
|
public void customStatusCode() {
|
||||||
RedirectView view = RedirectView
|
String url = "http://url.somewhere.com";
|
||||||
.builder("http://url.somewhere.com")
|
RedirectView view = new RedirectView(url, HttpStatus.FOUND);
|
||||||
.statusCode(HttpStatus.FOUND)
|
|
||||||
.build();
|
|
||||||
view.render(new HashMap<>(), MediaType.TEXT_HTML, exchange);
|
view.render(new HashMap<>(), MediaType.TEXT_HTML, exchange);
|
||||||
assertEquals(HttpStatus.FOUND, response.getStatusCode());
|
assertEquals(HttpStatus.FOUND, response.getStatusCode());
|
||||||
assertEquals(URI.create("http://url.somewhere.com"), response.getHeaders().getLocation());
|
assertEquals(URI.create(url), response.getHeaders().getLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -137,10 +135,8 @@ public class RedirectViewTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void propagateQueryParams() throws Exception {
|
public void propagateQueryParams() throws Exception {
|
||||||
RedirectView view = RedirectView
|
RedirectView view = new RedirectView("http://url.somewhere.com?foo=bar#bazz");
|
||||||
.builder("http://url.somewhere.com?foo=bar#bazz")
|
view.setPropagateQuery(true);
|
||||||
.propagateQueryParams(true)
|
|
||||||
.build();
|
|
||||||
request.setUri(URI.create("http://url.somewhere.com?a=b&c=d"));
|
request.setUri(URI.create("http://url.somewhere.com?a=b&c=d"));
|
||||||
view.render(new HashMap<>(), MediaType.TEXT_HTML, exchange);
|
view.render(new HashMap<>(), MediaType.TEXT_HTML, exchange);
|
||||||
assertEquals(HttpStatus.SEE_OTHER, response.getStatusCode());
|
assertEquals(HttpStatus.SEE_OTHER, response.getStatusCode());
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public class UrlBasedViewResolverTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void customizedRedirectView() throws Exception {
|
public void customizedRedirectView() throws Exception {
|
||||||
resolver.setRedirectViewProvider(url -> RedirectView.builder(url).statusCode(HttpStatus.FOUND).build());
|
resolver.setRedirectViewProvider(url -> new RedirectView(url, HttpStatus.FOUND));
|
||||||
Mono<View> mono = resolver.resolveViewName("redirect:foo", Locale.US);
|
Mono<View> mono = resolver.resolveViewName("redirect:foo", Locale.US);
|
||||||
assertNotNull(mono.block());
|
assertNotNull(mono.block());
|
||||||
StepVerifier.create(mono)
|
StepVerifier.create(mono)
|
||||||
|
|||||||
Reference in New Issue
Block a user