Skip to content

Commit

Permalink
Add status/headers to WebMVC FragmentsRendering
Browse files Browse the repository at this point in the history
  • Loading branch information
rstoyanchev committed Jul 10, 2024
1 parent 14c1faa commit 65296c6
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@

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;
Expand Down Expand Up @@ -96,6 +100,13 @@ public void handleReturnValue(@Nullable Object returnValue, MethodParameter retu
}

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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
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;
Expand All @@ -44,14 +46,34 @@
*/
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -31,9 +36,40 @@
*/
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));
Expand All @@ -58,7 +94,8 @@ public DefaultFragmentsRenderingBuilder fragments(Collection<ModelAndView> fragm

@Override
public FragmentsRendering build() {
return new DefaultFragmentsRendering(this.fragments);
return new DefaultFragmentsRendering(
this.status, (this.headers != null ? this.headers : HttpHeaders.EMPTY), this.fragments);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@

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;

Expand All @@ -34,6 +38,17 @@
*/
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
Expand Down Expand Up @@ -73,6 +88,29 @@ static Builder with(Collection<ModelAndView> fragments) {
*/
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
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;

Expand All @@ -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");
}

Expand Down Expand Up @@ -90,10 +91,13 @@ void handleViewInstance() throws Exception {

@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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -28,7 +27,6 @@
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;
Expand Down Expand Up @@ -59,9 +57,9 @@ void render() throws Exception {
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);
Expand Down

0 comments on commit 65296c6

Please sign in to comment.