Add RedirectView to Spring Web Reactive
Main differences with the Spring MVC original implementation: - Default redirect HTTP code is 303 See Other since we can assume all HTTP clients support HTTP 1.1 in 2016 - No more http10Compatible property, use statusCode instead - By default the redirect is relative to the context path - A builder allow to set various properties if needed - In UrlBasedViewResolver, a Function<String, RedirectView> redirectViewProvider property allows to customize RedirectView instances in a flexible way Issue: SPR-14534
This commit is contained in:
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.result.view;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
|
||||
/**
|
||||
* View that redirects to an absolute or context relative URL. The URL may be a URI
|
||||
* template in which case the URI template variables will be replaced with values
|
||||
* available in the model.
|
||||
*
|
||||
* <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,
|
||||
* {@link HttpStatus#FOUND} code can be set via {@link #setStatusCode(HttpStatus)}.
|
||||
*
|
||||
* <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
|
||||
* context path, while with this flag off they are considered relative to the web server
|
||||
* root.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @see #setContextRelative
|
||||
* @since 5.0
|
||||
*/
|
||||
public class RedirectView extends AbstractUrlBasedView {
|
||||
|
||||
private static final Pattern URI_TEMPLATE_VARIABLE_PATTERN = Pattern.compile("\\{([^/]+?)\\}");
|
||||
|
||||
|
||||
private boolean contextRelative = true;
|
||||
|
||||
private HttpStatus statusCode = HttpStatus.SEE_OTHER;
|
||||
|
||||
private boolean propagateQueryParams = false;
|
||||
|
||||
private String[] hosts;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code RedirectView} with the given redirect URL.
|
||||
*
|
||||
* @see #builder(String)
|
||||
*/
|
||||
public RedirectView(String redirectUrl) {
|
||||
super(redirectUrl);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a builder for a {@code RedirectView}.
|
||||
*/
|
||||
public static Builder builder(String redirectUrl) {
|
||||
return new BuilderImpl(redirectUrl);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to interpret a given URL that starts with a slash ("/")
|
||||
* as relative to the current context path.
|
||||
* <p>Default is "true": the context path will be
|
||||
* 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) {
|
||||
this.contextRelative = contextRelative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a customized redirect status code to be used for a redirect. Default is
|
||||
* {@link HttpStatus#SEE_OTHER} which is the correct code for HTTP 1.1
|
||||
* clients. This setter can be used to configure {@link HttpStatus#FOUND}
|
||||
* if HTTP 1.0 clients need to be supported, or any other {@literal 3xx}
|
||||
* status code.
|
||||
*/
|
||||
public void setStatusCode(HttpStatus statusCode) {
|
||||
Assert.notNull(statusCode);
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the redirect status code.
|
||||
*/
|
||||
public HttpStatus getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* When set to {@code true} the query string of the current URL is appended
|
||||
* and thus propagated through to the redirected URL.
|
||||
* <p>Defaults to {@code false}.
|
||||
*/
|
||||
public void setPropagateQueryParams(boolean propagateQueryParams) {
|
||||
this.propagateQueryParams = propagateQueryParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure one or more hosts associated with the application.
|
||||
* All other hosts will be considered external hosts.
|
||||
* <p>In effect, this property provides a way turn off encoding via
|
||||
* {@link javax.servlet.http.HttpServletResponse#encodeRedirectURL} for URLs that have a
|
||||
* host and that host is not listed as a known host when using a Servlet based engine.
|
||||
* <p>If not set (the default) all URLs are encoded through the response.
|
||||
* @param hosts one or more application hosts
|
||||
*/
|
||||
public void setHosts(String... hosts) {
|
||||
this.hosts = hosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert model to request parameters and redirect to the given URL.
|
||||
* @see #sendRedirect
|
||||
*/
|
||||
@Override
|
||||
protected Mono<Void> renderInternal(Map<String, Object> model, MediaType contentType,
|
||||
ServerWebExchange exchange) {
|
||||
String targetUrl = createTargetUrl(model, exchange);
|
||||
return sendRedirect(exchange, targetUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the target URL by checking if the redirect string is a URI template first,
|
||||
* expanding it with the given model, and then optionally appending simple type model
|
||||
* attributes as query String parameters.
|
||||
*/
|
||||
protected final String createTargetUrl(Map<String, Object> model, ServerWebExchange exchange) {
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
// Prepare target URL.
|
||||
StringBuilder targetUrl = new StringBuilder();
|
||||
if (this.contextRelative && getUrl().startsWith("/")) {
|
||||
// Do not apply context path to relative URLs.
|
||||
targetUrl.append(request.getContextPath());
|
||||
}
|
||||
targetUrl.append(getUrl());
|
||||
|
||||
Charset charset = this.getDefaultCharset();
|
||||
if (StringUtils.hasText(targetUrl)) {
|
||||
Map<String, String> variables = getCurrentRequestUriVariables(exchange);
|
||||
targetUrl = replaceUriTemplateVariables(targetUrl.toString(), model, variables, charset);
|
||||
}
|
||||
if (this.propagateQueryParams) {
|
||||
appendCurrentQueryParams(targetUrl, request);
|
||||
}
|
||||
|
||||
return targetUrl.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace URI template variables in the target URL with encoded model
|
||||
* attributes or URI variables from the current request. Model attributes
|
||||
* referenced in the URL are removed from the model.
|
||||
* @param targetUrl the redirect URL
|
||||
* @param model Map that contains model attributes
|
||||
* @param currentUriVariables current request URI variables to use
|
||||
* @param charset the charset to use
|
||||
*/
|
||||
protected StringBuilder replaceUriTemplateVariables(String targetUrl,
|
||||
Map<String, Object> model, Map<String, String> currentUriVariables, Charset charset) {
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
Matcher matcher = URI_TEMPLATE_VARIABLE_PATTERN.matcher(targetUrl);
|
||||
int endLastMatch = 0;
|
||||
while (matcher.find()) {
|
||||
String name = matcher.group(1);
|
||||
Object value = (model.containsKey(name) ? model.remove(name) : currentUriVariables.get(name));
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("Model has no value for key '" + name + "'");
|
||||
}
|
||||
result.append(targetUrl.substring(endLastMatch, matcher.start()));
|
||||
try {
|
||||
result.append(UriUtils.encodePathSegment(value.toString(), charset.name()));
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
endLastMatch = matcher.end();
|
||||
}
|
||||
result.append(targetUrl.substring(endLastMatch, targetUrl.length()));
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, String> getCurrentRequestUriVariables(ServerWebExchange exchange) {
|
||||
String name = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
|
||||
return (Map<String, String>) exchange.getAttribute(name).orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the query string of the current request to the target redirect URL.
|
||||
* @param targetUrl the StringBuilder to append the properties to
|
||||
* @param request the current request
|
||||
*/
|
||||
protected void appendCurrentQueryParams(StringBuilder targetUrl, ServerHttpRequest request) {
|
||||
String query = request.getURI().getQuery();
|
||||
if (StringUtils.hasText(query)) {
|
||||
// Extract anchor fragment, if any.
|
||||
String fragment = null;
|
||||
int anchorIndex = targetUrl.indexOf("#");
|
||||
if (anchorIndex > -1) {
|
||||
fragment = targetUrl.substring(anchorIndex);
|
||||
targetUrl.delete(anchorIndex, targetUrl.length());
|
||||
}
|
||||
|
||||
if (targetUrl.toString().indexOf('?') < 0) {
|
||||
targetUrl.append('?').append(query);
|
||||
}
|
||||
else {
|
||||
targetUrl.append('&').append(query);
|
||||
}
|
||||
// Append anchor fragment, if any, to end of URL.
|
||||
if (fragment != null) {
|
||||
targetUrl.append(fragment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a redirect back to the HTTP client
|
||||
* @param exchange current HTTP exchange
|
||||
* @param targetUrl the target URL to redirect to
|
||||
*/
|
||||
protected Mono<Void> sendRedirect(ServerWebExchange exchange, String targetUrl) {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
// TODO Support encoding redirect URL as ServerHttpResponse level when SPR-14529 will be fixed
|
||||
response.getHeaders().setLocation(URI.create(targetUrl));
|
||||
response.setStatusCode(this.statusCode);
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given targetUrl has a host that is a "foreign" system in which
|
||||
* case {@link javax.servlet.http.HttpServletResponse#encodeRedirectURL} will not be applied.
|
||||
* This method returns {@code true} if the {@link #setHosts(String[])}
|
||||
* property is configured and the target URL has a host that does not match.
|
||||
* @param targetUrl the target redirect URL
|
||||
* @return {@code true} the target URL has a remote host, {@code false} if it
|
||||
* the URL does not have a host or the "host" property is not configured.
|
||||
*/
|
||||
protected boolean isRemoteHost(String targetUrl) {
|
||||
if (ObjectUtils.isEmpty(this.hosts)) {
|
||||
return false;
|
||||
}
|
||||
String targetHost = UriComponentsBuilder.fromUriString(targetUrl).build().getHost();
|
||||
if (StringUtils.isEmpty(targetHost)) {
|
||||
return false;
|
||||
}
|
||||
for (String host : this.hosts) {
|
||||
if (targetHost.equals(host)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkResourceExists(Locale locale) throws Exception {
|
||||
return true;
|
||||
}
|
||||
|
||||
public interface Builder {
|
||||
|
||||
/**
|
||||
* @see RedirectView#setContextRelative(boolean)
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.web.reactive.result.view;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -56,6 +57,15 @@ import org.springframework.util.PatternMatchUtils;
|
||||
*/
|
||||
public class UrlBasedViewResolver extends ViewResolverSupport implements ViewResolver, InitializingBean {
|
||||
|
||||
/**
|
||||
* Prefix for special view names that specify a redirect URL (usually
|
||||
* to a controller after a form has been submitted and processed).
|
||||
* Such view names will not be resolved in the configured default
|
||||
* way but rather be treated as special shortcut.
|
||||
*/
|
||||
public static final String REDIRECT_URL_PREFIX = "redirect:";
|
||||
|
||||
|
||||
private Class<?> viewClass;
|
||||
|
||||
private String prefix = "";
|
||||
@@ -64,6 +74,8 @@ public class UrlBasedViewResolver extends ViewResolverSupport implements ViewRes
|
||||
|
||||
private String[] viewNames;
|
||||
|
||||
private Function<String, RedirectView> redirectViewProvider = url -> new RedirectView(url);
|
||||
|
||||
|
||||
/**
|
||||
* Set the view class to instantiate through {@link #createUrlBasedView(String)}.
|
||||
@@ -141,6 +153,13 @@ public class UrlBasedViewResolver extends ViewResolverSupport implements ViewRes
|
||||
return this.viewNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL based {@link RedirectView} provider which can be used to provide, for example,
|
||||
* redirect views with a custom default status code.
|
||||
*/
|
||||
public void setRedirectViewProvider(Function<String, RedirectView> redirectViewProvider) {
|
||||
this.redirectViewProvider = redirectViewProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
@@ -149,13 +168,19 @@ public class UrlBasedViewResolver extends ViewResolverSupport implements ViewRes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<View> resolveViewName(String viewName, Locale locale) {
|
||||
if (!canHandle(viewName, locale)) {
|
||||
return Mono.empty();
|
||||
}
|
||||
AbstractUrlBasedView urlBasedView = createUrlBasedView(viewName);
|
||||
AbstractUrlBasedView urlBasedView;
|
||||
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
|
||||
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
|
||||
urlBasedView = this.redirectViewProvider.apply(redirectUrl);
|
||||
}
|
||||
else {
|
||||
urlBasedView = createUrlBasedView(viewName);
|
||||
}
|
||||
View view = applyLifecycleMethods(viewName, urlBasedView);
|
||||
try {
|
||||
return (urlBasedView.checkResourceExists(locale) ? Mono.just(view) : Mono.empty());
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.result.view;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.DefaultWebSessionManager;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
/**
|
||||
* Tests for redirect view, and query string construction.
|
||||
* Doesn't test URL encoding, although it does check that it's called.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class RedirectViewTests {
|
||||
|
||||
private MockServerHttpRequest request;
|
||||
|
||||
private MockServerHttpResponse response;
|
||||
|
||||
private ServerWebExchange exchange;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
request = new MockServerHttpRequest();
|
||||
request.setContextPath("/context");
|
||||
response = new MockServerHttpResponse();
|
||||
WebSessionManager sessionManager = new DefaultWebSessionManager();
|
||||
exchange = new DefaultServerWebExchange(request, response, sessionManager);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void noUrlSet() throws Exception {
|
||||
RedirectView rv = new RedirectView(null);
|
||||
rv.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultStatusCode() {
|
||||
String url = "http://url.somewhere.com";
|
||||
RedirectView view = new RedirectView(url);
|
||||
view.render(new HashMap<>(), MediaType.TEXT_HTML, exchange);
|
||||
assertEquals(HttpStatus.SEE_OTHER, response.getStatusCode());
|
||||
assertEquals(URI.create(url), response.getHeaders().getLocation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customStatusCode() {
|
||||
RedirectView view = RedirectView
|
||||
.builder("http://url.somewhere.com")
|
||||
.statusCode(HttpStatus.FOUND)
|
||||
.build();
|
||||
view.render(new HashMap<>(), MediaType.TEXT_HTML, exchange);
|
||||
assertEquals(HttpStatus.FOUND, response.getStatusCode());
|
||||
assertEquals(URI.create("http://url.somewhere.com"), response.getHeaders().getLocation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextRelative() {
|
||||
String url = "/test.html";
|
||||
RedirectView view = new RedirectView(url);
|
||||
view.render(new HashMap<>(), MediaType.TEXT_HTML, exchange);
|
||||
assertEquals(URI.create("/context/test.html"), response.getHeaders().getLocation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextRelativeQueryParam() {
|
||||
String url = "/test.html?id=1";
|
||||
RedirectView view = new RedirectView(url);
|
||||
view.render(new HashMap<>(), MediaType.TEXT_HTML, exchange);
|
||||
assertEquals(URI.create("/context/test.html?id=1"), response.getHeaders().getLocation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remoteHost() {
|
||||
RedirectView view = new RedirectView("");
|
||||
|
||||
assertFalse(view.isRemoteHost("http://url.somewhere.com"));
|
||||
assertFalse(view.isRemoteHost("/path"));
|
||||
assertFalse(view.isRemoteHost("http://url.somewhereelse.com"));
|
||||
|
||||
view.setHosts(new String[] {"url.somewhere.com"});
|
||||
|
||||
assertFalse(view.isRemoteHost("http://url.somewhere.com"));
|
||||
assertFalse(view.isRemoteHost("/path"));
|
||||
assertTrue(view.isRemoteHost("http://url.somewhereelse.com"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandUriTemplateVariablesFromModel() {
|
||||
String url = "http://url.somewhere.com?foo={foo}";
|
||||
Map<String, String> model = Collections.singletonMap("foo", "bar");
|
||||
RedirectView view = new RedirectView(url);
|
||||
view.render(model, MediaType.TEXT_HTML, exchange);
|
||||
assertEquals(URI.create("http://url.somewhere.com?foo=bar"), response.getHeaders().getLocation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandUriTemplateVariablesFromExchangeAttribute() {
|
||||
String url = "http://url.somewhere.com?foo={foo}";
|
||||
Map<String, String> attributes = Collections.singletonMap("foo", "bar");
|
||||
exchange.getAttributes().put(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, attributes);
|
||||
RedirectView view = new RedirectView(url);
|
||||
view.render(new HashMap<>(), MediaType.TEXT_HTML, exchange);
|
||||
assertEquals(URI.create("http://url.somewhere.com?foo=bar"), response.getHeaders().getLocation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propagateQueryParams() throws Exception {
|
||||
RedirectView view = RedirectView
|
||||
.builder("http://url.somewhere.com?foo=bar#bazz")
|
||||
.propagateQueryParams(true)
|
||||
.build();
|
||||
request.setUri(URI.create("http://url.somewhere.com?a=b&c=d"));
|
||||
view.render(new HashMap<>(), MediaType.TEXT_HTML, exchange);
|
||||
assertEquals(HttpStatus.SEE_OTHER, response.getStatusCode());
|
||||
assertEquals(URI.create("http://url.somewhere.com?foo=bar&a=b&c=d#bazz"), response.getHeaders().getLocation());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,33 +19,40 @@ package org.springframework.web.reactive.result.view;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link UrlBasedViewResolver}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class UrlBasedViewResolverTests {
|
||||
|
||||
private UrlBasedViewResolver resolver;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
StaticApplicationContext context = new StaticApplicationContext();
|
||||
context.refresh();
|
||||
resolver = new UrlBasedViewResolver();
|
||||
resolver.setApplicationContext(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void viewNames() throws Exception {
|
||||
StaticApplicationContext context = new StaticApplicationContext();
|
||||
context.refresh();
|
||||
|
||||
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
|
||||
resolver.setViewClass(TestView.class);
|
||||
resolver.setViewNames("my*");
|
||||
resolver.setApplicationContext(context);
|
||||
|
||||
Mono<View> mono = resolver.resolveViewName("my-view", Locale.US);
|
||||
assertNotNull(mono.block());
|
||||
@@ -54,6 +61,34 @@ public class UrlBasedViewResolverTests {
|
||||
assertNull(mono.block());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void redirectView() throws Exception {
|
||||
Mono<View> mono = resolver.resolveViewName("redirect:foo", Locale.US);
|
||||
assertNotNull(mono.block());
|
||||
StepVerifier.create(mono)
|
||||
.consumeNextWith(view -> {
|
||||
assertEquals(RedirectView.class, view.getClass());
|
||||
RedirectView redirectView = (RedirectView) view;
|
||||
assertEquals(redirectView.getUrl(), "foo");
|
||||
assertEquals(redirectView.getStatusCode(), HttpStatus.SEE_OTHER);
|
||||
})
|
||||
.expectComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customizedRedirectView() throws Exception {
|
||||
resolver.setRedirectViewProvider(url -> RedirectView.builder(url).statusCode(HttpStatus.FOUND).build());
|
||||
Mono<View> mono = resolver.resolveViewName("redirect:foo", Locale.US);
|
||||
assertNotNull(mono.block());
|
||||
StepVerifier.create(mono)
|
||||
.consumeNextWith(view -> {
|
||||
assertEquals(RedirectView.class, view.getClass());
|
||||
RedirectView redirectView = (RedirectView) view;
|
||||
assertEquals(redirectView.getUrl(), "foo");
|
||||
assertEquals(redirectView.getStatusCode(), HttpStatus.FOUND);
|
||||
})
|
||||
.expectComplete();
|
||||
}
|
||||
|
||||
private static class TestView extends AbstractUrlBasedView {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user