Skip to content

Commit f919439

Browse files
committed
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
1 parent 4c0ece9 commit f919439

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-0
lines changed

spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseBuilder.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import org.springframework.util.Assert;
4040
import org.springframework.util.LinkedMultiValueMap;
4141
import org.springframework.util.MultiValueMap;
42+
import org.springframework.web.reactive.result.view.RedirectView;
43+
import org.springframework.web.reactive.result.view.View;
4244
import org.springframework.web.reactive.result.view.ViewResolver;
4345
import org.springframework.web.server.ServerWebExchange;
4446

@@ -195,13 +197,24 @@ protected Mono<Void> writeToInternal(ServerWebExchange exchange, Context context
195197
.switchIfEmpty(Mono.error(() ->
196198
new IllegalArgumentException("Could not resolve view with name '" + name() + "'")))
197199
.flatMap(view -> {
200+
setStatus(view);
198201
List<MediaType> mediaTypes = view.getSupportedMediaTypes();
199202
return view.render(model(),
200203
contentType == null && !mediaTypes.isEmpty() ? mediaTypes.get(0) : contentType,
201204
exchange);
202205
});
203206
}
204207

208+
private void setStatus(View view) {
209+
if (view instanceof RedirectView) {
210+
HttpStatus httpStatus = HttpStatus.resolve(rawStatusCode());
211+
if (httpStatus != null && httpStatus.is3xxRedirection()) {
212+
RedirectView redirectView = (RedirectView) view;
213+
redirectView.setStatusCode(httpStatus);
214+
}
215+
}
216+
}
217+
205218
}
206219

207220
}

spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseTests.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.web.reactive.function.server;
1818

19+
import java.time.Duration;
1920
import java.time.ZonedDateTime;
2021
import java.time.format.DateTimeFormatter;
2122
import java.time.temporal.ChronoUnit;
@@ -37,6 +38,7 @@
3738
import org.springframework.util.LinkedMultiValueMap;
3839
import org.springframework.util.MultiValueMap;
3940
import org.springframework.web.reactive.result.view.AbstractView;
41+
import org.springframework.web.reactive.result.view.RedirectView;
4042
import org.springframework.web.reactive.result.view.View;
4143
import org.springframework.web.reactive.result.view.ViewResolver;
4244
import org.springframework.web.reactive.result.view.ViewResolverSupport;
@@ -47,8 +49,10 @@
4749

4850
import static org.assertj.core.api.Assertions.assertThat;
4951
import static org.mockito.ArgumentMatchers.any;
52+
import static org.mockito.ArgumentMatchers.eq;
5053
import static org.mockito.BDDMockito.given;
5154
import static org.mockito.Mockito.mock;
55+
import static org.mockito.Mockito.verify;
5256

5357
/**
5458
* @author Arjen Poutsma
@@ -155,6 +159,41 @@ public void render() {
155159
.verify();
156160
}
157161

162+
@Test
163+
public void writeTo() {
164+
Map<String, Object> model = Collections.singletonMap("foo", "bar");
165+
RenderingResponse renderingResponse = RenderingResponse.create("view")
166+
.status(HttpStatus.FOUND)
167+
.modelAttributes(model)
168+
.build().block(Duration.of(5, ChronoUnit.MILLIS));
169+
assertThat(renderingResponse).isNotNull();
170+
171+
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("http://localhost"));
172+
MediaType contentType = MediaType.APPLICATION_PDF;
173+
exchange.getResponse().getHeaders().setContentType(contentType);
174+
175+
ViewResolver viewResolver = mock(ViewResolver.class);
176+
RedirectView view = mock(RedirectView.class);
177+
given(viewResolver.resolveViewName(eq("view"), any())).willReturn(Mono.just(view));
178+
given(view.render(model, contentType, exchange)).willReturn(Mono.empty());
179+
180+
List<ViewResolver> viewResolvers = new ArrayList<>();
181+
viewResolvers.add(viewResolver);
182+
183+
HandlerStrategies mockConfig = mock(HandlerStrategies.class);
184+
given(mockConfig.viewResolvers()).willReturn(viewResolvers);
185+
186+
ServerResponse.Context context = mock(ServerResponse.Context.class);
187+
given(context.viewResolvers()).willReturn(viewResolvers);
188+
189+
Mono<Void> result = renderingResponse.writeTo(exchange, context);
190+
StepVerifier.create(result)
191+
.expectComplete()
192+
.verify();
193+
194+
verify(view).setStatusCode(HttpStatus.FOUND);
195+
}
196+
158197
@Test
159198
public void defaultContentType() {
160199
Mono<RenderingResponse> result = RenderingResponse.create("view").build();

0 commit comments

Comments
 (0)