/** * Send a redirect back to the HTTP client. * @param targetUrl the target URL to redirect to * @param exchange current exchange */ protected Mono<Void> sendRedirect(String targetUrl, ServerWebExchange exchange) { String transformedUrl = (isRemoteHost(targetUrl) ? targetUrl : exchange.transformUrl(targetUrl)); ServerHttpResponse response = exchange.getResponse(); response.getHeaders().setLocation(URI.create(transformedUrl)); response.setStatusCode(getStatusCode()); return Mono.empty(); }
/** * Create the target URL and, if necessary, pre-pend the contextPath, expand * URI template variables, append the current request query, and apply the * configured {@link #getRequestDataValueProcessor() * RequestDataValueProcessor}. */ protected final String createTargetUrl(Map<String, Object> model, ServerWebExchange exchange) { String url = getUrl(); Assert.state(url != null, "'url' not set"); ServerHttpRequest request = exchange.getRequest(); StringBuilder targetUrl = new StringBuilder(); if (isContextRelative() && url.startsWith("/")) { targetUrl.append(request.getPath().contextPath().value()); } targetUrl.append(url); if (StringUtils.hasText(targetUrl)) { Map<String, String> uriVars = getCurrentUriVariables(exchange); targetUrl = expandTargetUrlTemplate(targetUrl.toString(), model, uriVars); } if (isPropagateQuery()) { targetUrl = appendCurrentRequestQuery(targetUrl.toString(), request); } String result = targetUrl.toString(); RequestDataValueProcessor processor = getRequestDataValueProcessor(); return (processor != null ? processor.processUrl(exchange, result) : result); }
@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("url.somewhere.com"); assertFalse(view.isRemoteHost("http://url.somewhere.com")); assertFalse(view.isRemoteHost("/path")); assertTrue(view.isRemoteHost("http://url.somewhereelse.com")); }
/** * Convert model to request parameters and redirect to the given URL. */ @Override protected Mono<Void> renderInternal( Map<String, Object> model, @Nullable MediaType contentType, ServerWebExchange exchange) { String targetUrl = createTargetUrl(model, exchange); return sendRedirect(targetUrl, exchange); }
@Test(expected = IllegalArgumentException.class) public void noUrlSet() throws Exception { RedirectView rv = new RedirectView(null); rv.afterPropertiesSet(); }
@Test public void defaultValuesForRedirect() throws Exception { Rendering rendering = Rendering.redirectTo("abc").build(); Object view = rendering.view(); assertEquals(RedirectView.class, view.getClass()); assertEquals("abc", ((RedirectView) view).getUrl()); assertTrue(((RedirectView) view).isContextRelative()); assertFalse(((RedirectView) view).isPropagateQuery()); }
@Test public void customizedRedirectView() throws Exception { this.resolver.setRedirectViewProvider(url -> new RedirectView(url, HttpStatus.FOUND)); Mono<View> mono = this.resolver.resolveViewName("redirect:foo", Locale.US); StepVerifier.create(mono) .consumeNextWith(view -> { assertEquals(RedirectView.class, view.getClass()); RedirectView redirectView = (RedirectView) view; assertEquals("foo", redirectView.getUrl()); assertEquals(HttpStatus.FOUND, redirectView.getStatusCode()); }) .expectComplete() .verify(Duration.ZERO); }
@Test public void expandUriTemplateVariablesFromExchangeAttribute() { String url = "http://url.somewhere.com?foo={foo}"; Map<String, String> attributes = Collections.singletonMap("foo", "bar"); this.exchange.getAttributes().put(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, attributes); RedirectView view = new RedirectView(url); view.render(new HashMap<>(), MediaType.TEXT_HTML, exchange).block(); assertEquals(URI.create("http://url.somewhere.com?foo=bar"), this.exchange.getResponse().getHeaders().getLocation()); }
@Test public void redirectView() throws Exception { Mono<View> mono = this.resolver.resolveViewName("redirect:foo", Locale.US); StepVerifier.create(mono) .consumeNextWith(view -> { assertEquals(RedirectView.class, view.getClass()); RedirectView redirectView = (RedirectView) view; assertEquals("foo", redirectView.getUrl()); assertEquals(HttpStatus.SEE_OTHER, redirectView.getStatusCode()); }) .expectComplete() .verify(Duration.ZERO); }
@Test public void propagateQueryParams() throws Exception { RedirectView view = new RedirectView("http://url.somewhere.com?foo=bar#bazz"); view.setPropagateQuery(true); this.exchange = MockServerWebExchange.from(MockServerHttpRequest.get("http://url.somewhere.com?a=b&c=d")); view.render(new HashMap<>(), MediaType.TEXT_HTML, this.exchange).block(); assertEquals(HttpStatus.SEE_OTHER, this.exchange.getResponse().getStatusCode()); assertEquals(URI.create("http://url.somewhere.com?foo=bar&a=b&c=d#bazz"), this.exchange.getResponse().getHeaders().getLocation()); }
@Test public void redirectWithAbsoluteUrl() throws Exception { Rendering rendering = Rendering.redirectTo("foo").contextRelative(false).build(); Object view = rendering.view(); assertEquals(RedirectView.class, view.getClass()); assertFalse(((RedirectView) view).isContextRelative()); }
@Test public void redirectWithPropagateQuery() throws Exception { Rendering rendering = Rendering.redirectTo("foo").propagateQuery(true).build(); Object view = rendering.view(); assertEquals(RedirectView.class, view.getClass()); assertTrue(((RedirectView) view).isPropagateQuery()); }
@Override public void afterPropertiesSet() throws Exception { super.afterPropertiesSet(); if (getStatusCode() == null) { throw new IllegalArgumentException("Property 'statusCode' is required"); } }
/** * Expand URI template variables in the target URL with either model * attribute values or as a fallback with URI variable values from the * current request. Values are encoded. */ protected StringBuilder expandTargetUrlTemplate(String targetUrl, Map<String, Object> model, Map<String, String> uriVariables) { Matcher matcher = URI_TEMPLATE_VARIABLE_PATTERN.matcher(targetUrl); boolean found = matcher.find(); if (!found) { return new StringBuilder(targetUrl); } StringBuilder result = new StringBuilder(); int endLastMatch = 0; while (found) { String name = matcher.group(1); Object value = (model.containsKey(name) ? model.get(name) : uriVariables.get(name)); Assert.notNull(value, () -> "No value for URI variable '" + name + "'"); result.append(targetUrl.substring(endLastMatch, matcher.start())); result.append(encodeUriVariable(value.toString())); endLastMatch = matcher.end(); found = matcher.find(); } result.append(targetUrl.substring(endLastMatch, targetUrl.length())); return result; }
/** * Create a new builder for a redirect through a {@link RedirectView}. * @param url the redirect URL * @return the builder */ static RedirectBuilder redirectTo(String url) { return new DefaultRenderingBuilder(new RedirectView(url)); }
@Test public void contextRelativeQueryParam() { String url = "/test.html?id=1"; RedirectView view = new RedirectView(url); view.render(new HashMap<>(), MediaType.TEXT_HTML, this.exchange).block(); assertEquals(URI.create("/context/test.html?id=1"), this.exchange.getResponse().getHeaders().getLocation()); }
/** * Convert model to request parameters and redirect to the given URL. */ @Override protected Mono<Void> renderInternal(Map<String, Object> model, MediaType contentType, ServerWebExchange exchange) { String targetUrl = createTargetUrl(model, exchange); return sendRedirect(targetUrl, exchange); }
/** * Expand URI template variables in the target URL with either model * attribute values or as a fallback with URI variable values from the * current request. Values are encoded. */ protected StringBuilder expandTargetUrlTemplate(String targetUrl, Map<String, Object> model, Map<String, String> uriVariables) { Matcher matcher = URI_TEMPLATE_VARIABLE_PATTERN.matcher(targetUrl); boolean found = matcher.find(); if (!found) { return new StringBuilder(targetUrl); } StringBuilder result = new StringBuilder(); int endLastMatch = 0; while (found) { String name = matcher.group(1); Object value = (model.containsKey(name) ? model.get(name) : uriVariables.get(name)); Assert.notNull(value, "No value for URI variable '" + name + "'"); result.append(targetUrl.substring(endLastMatch, matcher.start())); result.append(encodeUriVariable(value.toString())); endLastMatch = matcher.end(); found = matcher.find(); } result.append(targetUrl.substring(endLastMatch, targetUrl.length())); return result; }
/** * Create the target URL and, if necessary, pre-pend the contextPath, expand * URI template variables, append the current request query, and apply the * configured {@link #getRequestDataValueProcessor() * RequestDataValueProcessor}. */ protected final String createTargetUrl(Map<String, Object> model, ServerWebExchange exchange) { StringBuilder targetUrl = new StringBuilder(); if (isContextRelative() && getUrl().startsWith("/")) { targetUrl.append(exchange.getRequest().getContextPath()); } targetUrl.append(getUrl()); if (StringUtils.hasText(targetUrl)) { Map<String, String> uriVars = getCurrentUriVariables(exchange); targetUrl = expandTargetUrlTemplate(targetUrl.toString(), model, uriVars); } if (isPropagateQuery()) { targetUrl = appendCurrentRequestQuery(targetUrl.toString(), exchange.getRequest()); } String result = targetUrl.toString(); RequestDataValueProcessor processor = getRequestDataValueProcessor(); return (processor != null ? processor.processUrl(exchange, result) : result); }
/** * Send a redirect back to the HTTP client * @param targetUrl the target URL to redirect to * @param exchange current exchange */ protected Mono<Void> sendRedirect(String targetUrl, ServerWebExchange exchange) { ServerHttpResponse response = exchange.getResponse(); String encodedURL = (isRemoteHost(targetUrl) ? targetUrl : response.encodeUrl(targetUrl)); response.getHeaders().setLocation(URI.create(encodedURL)); response.setStatusCode(getStatusCode()); return Mono.empty(); }