From 0d64c90a79a5d390f73d01f7c77928c0d958bd2f Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Fri, 9 Aug 2024 17:59:50 +0300 Subject: [PATCH] Move status/header handling into FragmentsRendering Based on feedback from htmx-spring-boot. It's more generally useful to process those from within DefaultFragmentsRendering rather than in ModelAndViewMethodReturnValueHandler. That way a custom return value handler can create a FragmentsRendering as well and get the same result. See gh-33194 --- .../ModelAndViewMethodReturnValueHandler.java | 11 ----------- .../web/servlet/view/DefaultFragmentsRendering.java | 8 ++++++++ .../ModelAndViewMethodReturnValueHandlerTests.java | 5 +---- .../servlet/view/DefaultFragmentsRenderingTests.java | 3 ++- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java index 5968894287..11dfd8fcdd 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java @@ -18,12 +18,8 @@ package org.springframework.web.servlet.mvc.method.annotation; import java.util.Collection; -import jakarta.servlet.http.HttpServletResponse; - import org.springframework.core.MethodParameter; -import org.springframework.http.HttpHeaders; import org.springframework.lang.Nullable; -import org.springframework.util.Assert; import org.springframework.util.PatternMatchUtils; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; @@ -100,13 +96,6 @@ public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturn } if (returnValue instanceof FragmentsRendering rendering) { - mavContainer.setStatus(rendering.status()); - HttpHeaders headers = rendering.headers(); - if (!headers.isEmpty()) { - HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); - Assert.state(response != null, "No HttpServletResponse"); - headers.forEach((name, values) -> values.forEach(value -> response.addHeader(name, value))); - } mavContainer.setView(rendering); return; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/DefaultFragmentsRendering.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/DefaultFragmentsRendering.java index a17adc76ee..a48691b5cd 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/DefaultFragmentsRendering.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/DefaultFragmentsRendering.java @@ -101,6 +101,14 @@ final class DefaultFragmentsRendering implements FragmentsRendering { @Nullable Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { + if (this.status != null) { + response.setStatus(this.status.value()); + } + + if (!this.headers.isEmpty()) { + this.headers.forEach((name, values) -> values.forEach(value -> response.addHeader(name, value))); + } + if (model != null) { model.forEach((key, value) -> this.modelAndViews.forEach(mv -> mv.getModel().putIfAbsent(key, value))); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java index 0b89f6200b..d48b682cc7 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java @@ -91,13 +91,10 @@ class ModelAndViewMethodReturnValueHandlerTests { @Test void handleFragmentsRendering() throws Exception { - FragmentsRendering rendering = FragmentsRendering.with("viewName") - .header("headerName", "headerValue") - .build(); + FragmentsRendering rendering = FragmentsRendering.with("viewName").build(); handler.handleReturnValue(rendering, returnParamModelAndView, mavContainer, webRequest); assertThat(mavContainer.getView()).isInstanceOf(SmartView.class); - assertThat(this.webRequest.getResponse().getHeader("headerName")).isEqualTo("headerValue"); } @Test diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/DefaultFragmentsRenderingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/DefaultFragmentsRenderingTests.java index 21b35574e0..0031ddb84c 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/DefaultFragmentsRenderingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/DefaultFragmentsRenderingTests.java @@ -43,7 +43,6 @@ import static org.junit.jupiter.api.condition.JRE.JAVA_21; @DisabledForJreRange(min = JAVA_21, disabledReason = "Kotlin doesn't support Java 21+ yet") public class DefaultFragmentsRenderingTests { - @Test void render() throws Exception { @@ -59,11 +58,13 @@ public class DefaultFragmentsRenderingTests { FragmentsRendering view = FragmentsRendering.with("fragment1", Map.of("foo", "Foo")) .fragment("fragment2", Map.of("bar", "Bar")) + .header("headerName", "headerValue") .build(); view.resolveNestedViews(viewResolver, Locale.ENGLISH); view.render(Collections.emptyMap(), request, response); + assertThat(response.getHeader("headerName")).isEqualTo("headerValue"); assertThat(response.getContentAsString()).isEqualTo("""

Hello Foo