diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RedirectView.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RedirectView.java index 2882a4f6b76..a9488b5dd9b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RedirectView.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RedirectView.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -169,6 +169,11 @@ public class RedirectView extends AbstractUrlBasedView { } + @Override + public boolean isRedirectView() { + return true; + } + @Override public boolean checkResourceExists(Locale locale) throws Exception { return true; diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/View.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/View.java index b35c2401732..61089fe7528 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/View.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/View.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -47,6 +47,13 @@ public interface View { */ List getSupportedMediaTypes(); + /** + * Whether this View does rendering by performing a redirect. + */ + default boolean isRedirectView() { + return false; + } + /** * Render the view based on the given {@link HandlerResult}. Implementations * can access and use the model or only a specific attribute in it. diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java index cce5d2ab6b2..c8b0b87bcf2 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java @@ -310,6 +310,12 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport private Mono render(List views, Map model, ServerWebExchange exchange) { + for (View view : views) { + if (view.isRedirectView()) { + return view.render(model, null, exchange); + } + } + List mediaTypes = getMediaTypes(views); MediaType bestMediaType = selectMediaType(exchange, () -> mediaTypes); if (bestMediaType != null) { diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java index 2810011bb0d..7e5b89a8c8d 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java @@ -33,6 +33,7 @@ import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import rx.Completable; +import org.springframework.context.support.StaticApplicationContext; import org.springframework.core.MethodParameter; import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; @@ -42,6 +43,7 @@ import org.springframework.core.io.buffer.support.DataBufferTestUtils; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse; import org.springframework.mock.http.server.reactive.test.MockServerWebExchange; import org.springframework.ui.ConcurrentModel; import org.springframework.ui.Model; @@ -194,11 +196,10 @@ public class ViewResolutionResultHandlerTests { @Test public void handleWithMultipleResolvers() throws Exception { - Object returnValue = "profile"; - MethodParameter returnType = on(Handler.class).annotNotPresent(ModelAttribute.class).resolveReturnType(String.class); - ViewResolver[] resolvers = {new TestViewResolver("account"), new TestViewResolver("profile")}; - - testHandle("/account", returnType, returnValue, "profile: {id=123}", resolvers); + testHandle("/account", + on(Handler.class).annotNotPresent(ModelAttribute.class).resolveReturnType(String.class), + "profile", "profile: {id=123}", + new TestViewResolver("account"), new TestViewResolver("profile")); } @Test @@ -280,6 +281,25 @@ public class ViewResolutionResultHandlerTests { .verify(); } + @Test // SPR-15291 + public void contentNegotiationWithRedirect() throws Exception { + + HandlerResult handlerResult = new HandlerResult(new Object(), "redirect:/", + on(Handler.class).annotNotPresent(ModelAttribute.class).resolveReturnType(String.class), + this.bindingContext); + + UrlBasedViewResolver viewResolver = new UrlBasedViewResolver(); + viewResolver.setApplicationContext(new StaticApplicationContext()); + ViewResolutionResultHandler resultHandler = resultHandler(viewResolver); + + MockServerWebExchange exchange = get("/account").accept(APPLICATION_JSON).toExchange(); + resultHandler.handleResult(exchange, handlerResult).block(Duration.ZERO); + + MockServerHttpResponse response = exchange.getResponse(); + assertEquals(303, response.getStatusCode().value()); + assertEquals("/", response.getHeaders().getLocation().toString()); + } + private ViewResolutionResultHandler resultHandler(ViewResolver... resolvers) { return resultHandler(Collections.emptyList(), resolvers);