From 718162b8c428220f4cb9c86092c9c651dec6e71c Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 24 May 2017 16:28:17 -0400 Subject: [PATCH] Fix issue with "redirect:" when a media type is present Issue: SPR-15291 --- .../reactive/result/view/RedirectView.java | 7 ++++- .../web/reactive/result/view/View.java | 9 +++++- .../view/ViewResolutionResultHandler.java | 6 ++++ .../ViewResolutionResultHandlerTests.java | 30 +++++++++++++++---- 4 files changed, 45 insertions(+), 7 deletions(-) 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 2882a4f6b765..a9488b5dd9b3 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 void afterPropertiesSet() throws Exception { } + @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 b35c24017324..61089fe7528f 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 cce5d2ab6b22..c8b0b87bcf29 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 @@ private boolean isBindingCandidate(String name, Object value) { 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 2810011bb0da..7e5b89a8c8d7 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.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.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 void handleReturnValueTypes() throws Exception { @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 void contentNegotiationWith406() throws Exception { .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);