From f91943977cd7775dd6196d4a0f4095dc9a098837 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Tue, 30 Aug 2022 15:14:03 +0200 Subject: [PATCH] RenderingResponse must set status code on RedirectView This commit makes sure that WebFlux's RenderingResponse sets the HTTP status code when rendering a RedirectView. Closes: gh-28839 --- .../DefaultRenderingResponseBuilder.java | 13 +++++++ .../server/DefaultRenderingResponseTests.java | 39 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseBuilder.java index a67ae486532f..7b8439f4f138 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseBuilder.java @@ -39,6 +39,8 @@ import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.result.view.RedirectView; +import org.springframework.web.reactive.result.view.View; import org.springframework.web.reactive.result.view.ViewResolver; import org.springframework.web.server.ServerWebExchange; @@ -195,6 +197,7 @@ protected Mono writeToInternal(ServerWebExchange exchange, Context context .switchIfEmpty(Mono.error(() -> new IllegalArgumentException("Could not resolve view with name '" + name() + "'"))) .flatMap(view -> { + setStatus(view); List mediaTypes = view.getSupportedMediaTypes(); return view.render(model(), contentType == null && !mediaTypes.isEmpty() ? mediaTypes.get(0) : contentType, @@ -202,6 +205,16 @@ protected Mono writeToInternal(ServerWebExchange exchange, Context context }); } + private void setStatus(View view) { + if (view instanceof RedirectView) { + HttpStatus httpStatus = HttpStatus.resolve(rawStatusCode()); + if (httpStatus != null && httpStatus.is3xxRedirection()) { + RedirectView redirectView = (RedirectView) view; + redirectView.setStatusCode(httpStatus); + } + } + } + } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseTests.java index 95952c55f3ab..987a2a855881 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseTests.java @@ -16,6 +16,7 @@ package org.springframework.web.reactive.function.server; +import java.time.Duration; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; @@ -37,6 +38,7 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.result.view.AbstractView; +import org.springframework.web.reactive.result.view.RedirectView; import org.springframework.web.reactive.result.view.View; import org.springframework.web.reactive.result.view.ViewResolver; import org.springframework.web.reactive.result.view.ViewResolverSupport; @@ -47,8 +49,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; /** * @author Arjen Poutsma @@ -155,6 +159,41 @@ public void render() { .verify(); } + @Test + public void writeTo() { + Map model = Collections.singletonMap("foo", "bar"); + RenderingResponse renderingResponse = RenderingResponse.create("view") + .status(HttpStatus.FOUND) + .modelAttributes(model) + .build().block(Duration.of(5, ChronoUnit.MILLIS)); + assertThat(renderingResponse).isNotNull(); + + MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("http://localhost")); + MediaType contentType = MediaType.APPLICATION_PDF; + exchange.getResponse().getHeaders().setContentType(contentType); + + ViewResolver viewResolver = mock(ViewResolver.class); + RedirectView view = mock(RedirectView.class); + given(viewResolver.resolveViewName(eq("view"), any())).willReturn(Mono.just(view)); + given(view.render(model, contentType, exchange)).willReturn(Mono.empty()); + + List viewResolvers = new ArrayList<>(); + viewResolvers.add(viewResolver); + + HandlerStrategies mockConfig = mock(HandlerStrategies.class); + given(mockConfig.viewResolvers()).willReturn(viewResolvers); + + ServerResponse.Context context = mock(ServerResponse.Context.class); + given(context.viewResolvers()).willReturn(viewResolvers); + + Mono result = renderingResponse.writeTo(exchange, context); + StepVerifier.create(result) + .expectComplete() + .verify(); + + verify(view).setStatusCode(HttpStatus.FOUND); + } + @Test public void defaultContentType() { Mono result = RenderingResponse.create("view").build();