Add status/headers to WebMVC FragmentsRendering

See gh-33162
This commit is contained in:
rstoyanchev
2024-07-10 16:40:13 +01:00
parent 14c1faa5ee
commit 65296c6aad
6 changed files with 120 additions and 10 deletions

View File

@@ -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;
}

View File

@@ -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;

View File

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

View File

@@ -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