Browse Source

Add status/headers to WebMVC FragmentsRendering

See gh-33162
pull/33213/head
rstoyanchev 1 year ago
parent
commit
65296c6aad
  1. 11
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java
  2. 26
      spring-webmvc/src/main/java/org/springframework/web/servlet/view/DefaultFragmentsRendering.java
  3. 39
      spring-webmvc/src/main/java/org/springframework/web/servlet/view/DefaultFragmentsRenderingBuilder.java
  4. 38
      spring-webmvc/src/main/java/org/springframework/web/servlet/view/FragmentsRendering.java
  5. 8
      spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java
  6. 8
      spring-webmvc/src/test/java/org/springframework/web/servlet/view/DefaultFragmentsRenderingTests.java

11
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java

@ -18,8 +18,12 @@ package org.springframework.web.servlet.mvc.method.annotation; @@ -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 @@ -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;
}

26
spring-webmvc/src/main/java/org/springframework/web/servlet/view/DefaultFragmentsRendering.java

@ -29,6 +29,8 @@ import jakarta.servlet.http.HttpServletRequest; @@ -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; @@ -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;

39
spring-webmvc/src/main/java/org/springframework/web/servlet/view/DefaultFragmentsRenderingBuilder.java

@ -17,9 +17,14 @@ @@ -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; @@ -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 @@ -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);
}
}

38
spring-webmvc/src/main/java/org/springframework/web/servlet/view/FragmentsRendering.java

@ -18,7 +18,11 @@ package org.springframework.web.servlet.view; @@ -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; @@ -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 { @@ -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

8
spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java

@ -33,6 +33,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap; @@ -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 { @@ -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 { @@ -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

8
spring-webmvc/src/test/java/org/springframework/web/servlet/view/DefaultFragmentsRenderingTests.java

@ -17,7 +17,6 @@ @@ -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 @@ -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 { @@ -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);

Loading…
Cancel
Save