Add status/headers to WebMVC FragmentsRendering
See gh-33162
This commit is contained in:
@@ -18,8 +18,12 @@ 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;
|
||||
@@ -96,6 +100,13 @@ 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;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
@@ -44,14 +46,34 @@ import org.springframework.web.servlet.ViewResolver;
|
||||
*/
|
||||
final class DefaultFragmentsRendering implements FragmentsRendering {
|
||||
|
||||
@Nullable
|
||||
private final HttpStatusCode status;
|
||||
|
||||
private final HttpHeaders headers;
|
||||
|
||||
private final Collection<ModelAndView> modelAndViews;
|
||||
|
||||
|
||||
DefaultFragmentsRendering(Collection<ModelAndView> modelAndViews) {
|
||||
this.modelAndViews = new ArrayList<>(modelAndViews);
|
||||
DefaultFragmentsRendering(
|
||||
@Nullable HttpStatusCode status, HttpHeaders headers, Collection<ModelAndView> fragments) {
|
||||
|
||||
this.status = status;
|
||||
this.headers = headers;
|
||||
this.modelAndViews = new ArrayList<>(fragments);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public HttpStatusCode status() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders headers() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRedirectView() {
|
||||
return false;
|
||||
|
||||
@@ -17,9 +17,14 @@
|
||||
package org.springframework.web.servlet.view;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
@@ -31,9 +36,40 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
*/
|
||||
final class DefaultFragmentsRenderingBuilder implements FragmentsRendering.Builder {
|
||||
|
||||
@Nullable
|
||||
private HttpStatusCode status;
|
||||
|
||||
@Nullable
|
||||
private HttpHeaders headers;
|
||||
|
||||
private final Collection<ModelAndView> fragments = new ArrayList<>();
|
||||
|
||||
|
||||
@Override
|
||||
public FragmentsRendering.Builder status(HttpStatusCode status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FragmentsRendering.Builder header(String headerName, String... headerValues) {
|
||||
initHeaders().put(headerName, Arrays.asList(headerValues));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FragmentsRendering.Builder headers(Consumer<HttpHeaders> headersConsumer) {
|
||||
headersConsumer.accept(initHeaders());
|
||||
return this;
|
||||
}
|
||||
|
||||
private HttpHeaders initHeaders() {
|
||||
if (this.headers == null) {
|
||||
this.headers = new HttpHeaders();
|
||||
}
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultFragmentsRenderingBuilder fragment(String viewName, Map<String, Object> model) {
|
||||
return fragment(new ModelAndView(viewName, model));
|
||||
@@ -58,7 +94,8 @@ final class DefaultFragmentsRenderingBuilder implements FragmentsRendering.Build
|
||||
|
||||
@Override
|
||||
public FragmentsRendering build() {
|
||||
return new DefaultFragmentsRendering(this.fragments);
|
||||
return new DefaultFragmentsRendering(
|
||||
this.status, (this.headers != null ? this.headers : HttpHeaders.EMPTY), this.fragments);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,11 @@ package org.springframework.web.servlet.view;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.SmartView;
|
||||
|
||||
@@ -34,6 +38,17 @@ import org.springframework.web.servlet.SmartView;
|
||||
*/
|
||||
public interface FragmentsRendering extends SmartView {
|
||||
|
||||
/**
|
||||
* Return the HTTP status to set the response to.
|
||||
*/
|
||||
@Nullable
|
||||
HttpStatusCode status();
|
||||
|
||||
/**
|
||||
* Return headers to add to the response.
|
||||
*/
|
||||
HttpHeaders headers();
|
||||
|
||||
|
||||
/**
|
||||
* Create a builder for {@link FragmentsRendering}, adding a fragment with
|
||||
@@ -73,6 +88,29 @@ public interface FragmentsRendering extends SmartView {
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* Specify the status to use for the response.
|
||||
* @param status the status to set
|
||||
* @return this builder
|
||||
*/
|
||||
Builder status(HttpStatusCode status);
|
||||
|
||||
/**
|
||||
* Add the given, single header value under the given name.
|
||||
* @param headerName the header name
|
||||
* @param headerValues the header value(s)
|
||||
* @return this builder
|
||||
*/
|
||||
Builder header(String headerName, String... headerValues);
|
||||
|
||||
/**
|
||||
* Provides access to every header declared so far with the possibility
|
||||
* to add, replace, or remove values.
|
||||
* @param headersConsumer the consumer to provide access to
|
||||
* @return this builder
|
||||
*/
|
||||
Builder headers(Consumer<HttpHeaders> headersConsumer);
|
||||
|
||||
/**
|
||||
* Add a fragment with a view name and a model.
|
||||
* @param viewName the name of the view for the fragment
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap;
|
||||
import org.springframework.web.servlet.view.FragmentsRendering;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
|
||||
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -56,7 +57,7 @@ class ModelAndViewMethodReturnValueHandlerTests {
|
||||
void setup() throws Exception {
|
||||
this.handler = new ModelAndViewMethodReturnValueHandler();
|
||||
this.mavContainer = new ModelAndViewContainer();
|
||||
this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||
this.webRequest = new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse());
|
||||
this.returnParamModelAndView = getReturnValueParam("modelAndView");
|
||||
}
|
||||
|
||||
@@ -90,10 +91,13 @@ class ModelAndViewMethodReturnValueHandlerTests {
|
||||
|
||||
@Test
|
||||
void handleFragmentsRendering() throws Exception {
|
||||
FragmentsRendering rendering = FragmentsRendering.with("viewName").build();
|
||||
FragmentsRendering rendering = FragmentsRendering.with("viewName")
|
||||
.header("headerName", "headerValue")
|
||||
.build();
|
||||
|
||||
handler.handleReturnValue(rendering, returnParamModelAndView, mavContainer, webRequest);
|
||||
assertThat(mavContainer.getView()).isInstanceOf(SmartView.class);
|
||||
assertThat(this.webRequest.getResponse().getHeader("headerName")).isEqualTo("headerValue");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.web.servlet.view;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -28,7 +27,6 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.ResourceBundleMessageSource;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.view.script.ScriptTemplateConfigurer;
|
||||
import org.springframework.web.servlet.view.script.ScriptTemplateViewResolver;
|
||||
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
|
||||
@@ -59,9 +57,9 @@ public class DefaultFragmentsRenderingTests {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
DefaultFragmentsRendering view = new DefaultFragmentsRendering(List.of(
|
||||
new ModelAndView("fragment1", Map.of("foo", "Foo")),
|
||||
new ModelAndView("fragment2", Map.of("bar", "Bar"))));
|
||||
FragmentsRendering view = FragmentsRendering.with("fragment1", Map.of("foo", "Foo"))
|
||||
.fragment("fragment2", Map.of("bar", "Bar"))
|
||||
.build();
|
||||
|
||||
view.resolveNestedViews(viewResolver, Locale.ENGLISH);
|
||||
view.render(Collections.emptyMap(), request, response);
|
||||
|
||||
Reference in New Issue
Block a user