Browse Source

Rename methods in FragmentsRendering

Previous commit 81ea35c726 in main for 7.0
should have been applied in 6.2.x first for 6.2.1.

This commit applies the changes in 6.2.x as intended,
effective as of 6.2.13.

Closes gh-33974
pull/35732/head
rstoyanchev 2 months ago
parent
commit
715bc68c16
  1. 4
      framework-docs/modules/ROOT/pages/web/webflux-view.adoc
  2. 4
      framework-docs/modules/ROOT/pages/web/webmvc-view/mvc-fragments.adoc
  3. 18
      spring-webflux/src/main/java/org/springframework/web/reactive/result/view/DefaultFragmentsRenderingBuilder.java
  4. 135
      spring-webflux/src/main/java/org/springframework/web/reactive/result/view/FragmentsRendering.java
  5. 4
      spring-webflux/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java
  6. 4
      spring-webflux/src/test/java/org/springframework/web/reactive/result/view/FragmentViewResolutionResultHandlerTests.java
  7. 2
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java
  8. 2
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java
  9. 85
      spring-webmvc/src/main/java/org/springframework/web/servlet/view/FragmentsRendering.java
  10. 2
      spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java
  11. 2
      spring-webmvc/src/test/java/org/springframework/web/servlet/view/DefaultFragmentsRenderingTests.java

4
framework-docs/modules/ROOT/pages/web/webflux-view.adoc

@ -448,7 +448,7 @@ Java:: @@ -448,7 +448,7 @@ Java::
----
@GetMapping
FragmentsRendering handle() {
return FragmentsRendering.with("posts").fragment("comments").build();
return FragmentsRendering.fragment("posts").fragment("comments").build();
}
----
@ -458,7 +458,7 @@ Kotlin:: @@ -458,7 +458,7 @@ Kotlin::
----
@GetMapping
fun handle(): FragmentsRendering {
return FragmentsRendering.with("posts").fragment("comments").build()
return FragmentsRendering.fragment("posts").fragment("comments").build()
}
----
======

4
framework-docs/modules/ROOT/pages/web/webmvc-view/mvc-fragments.adoc

@ -48,7 +48,7 @@ Java:: @@ -48,7 +48,7 @@ Java::
----
@GetMapping
FragmentsRendering handle() {
return FragmentsRendering.with("posts").fragment("comments").build();
return FragmentsRendering.fragment("posts").fragment("comments").build();
}
----
@ -58,7 +58,7 @@ Kotlin:: @@ -58,7 +58,7 @@ Kotlin::
----
@GetMapping
fun handle(): FragmentsRendering {
return FragmentsRendering.with("posts").fragment("comments").build()
return FragmentsRendering.fragment("posts").fragment("comments").build()
}
----
======

18
spring-webflux/src/main/java/org/springframework/web/reactive/result/view/DefaultFragmentsRenderingBuilder.java

@ -49,8 +49,8 @@ class DefaultFragmentsRenderingBuilder implements FragmentsRendering.Builder { @@ -49,8 +49,8 @@ class DefaultFragmentsRenderingBuilder implements FragmentsRendering.Builder {
@Nullable
private HttpHeaders headers;
DefaultFragmentsRenderingBuilder(Collection<Fragment> fragments) {
this.fragmentsCollection = new ArrayList<>(fragments);
DefaultFragmentsRenderingBuilder() {
this.fragmentsCollection = null;
this.fragmentsFlux = null;
}
@ -85,13 +85,13 @@ class DefaultFragmentsRenderingBuilder implements FragmentsRendering.Builder { @@ -85,13 +85,13 @@ class DefaultFragmentsRenderingBuilder implements FragmentsRendering.Builder {
}
@Override
public FragmentsRendering.Builder fragment(String viewName, Map<String, Object> model) {
return fragment(Fragment.create(viewName, model));
public FragmentsRendering.Builder fragment(String viewName) {
return fragment(Fragment.create(viewName));
}
@Override
public FragmentsRendering.Builder fragment(String viewName) {
return fragment(Fragment.create(viewName));
public FragmentsRendering.Builder fragment(String viewName, Map<String, Object> model) {
return fragment(Fragment.create(viewName, model));
}
@Override
@ -100,6 +100,12 @@ class DefaultFragmentsRenderingBuilder implements FragmentsRendering.Builder { @@ -100,6 +100,12 @@ class DefaultFragmentsRenderingBuilder implements FragmentsRendering.Builder {
return this;
}
@Override
public FragmentsRendering.Builder fragments(Collection<Fragment> fragments) {
initFragmentsCollection().addAll(fragments);
return this;
}
private Collection<Fragment> initFragmentsCollection() {
if (this.fragmentsCollection == null) {
this.fragmentsCollection = new ArrayList<>();

135
spring-webflux/src/main/java/org/springframework/web/reactive/result/view/FragmentsRendering.java

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
package org.springframework.web.reactive.result.view;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@ -32,13 +31,16 @@ import org.springframework.lang.Nullable; @@ -32,13 +31,16 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Public API for HTML rendering of a collection of fragments each with a view
* and independent model. For use with frontends technologies such as
* Public API to render HTML fragments. A fragment is a portion of an HTML page.
* Normally HTML is rendered with a single model and view. This API allows
* using multiple model and view pairs, one for each HTML fragment.
*
* <p>For use with frontends technologies such as
* <a href="https://htmx.org/">htmx</a> where multiple page fragments may be
* rendered in one response. Supported as a return value from Spring WebFlux
* controller methods.
* rendered in one response.
*
* <p>For full page rendering with a single model and view, use {@link Rendering}.
* <p>Supported as a return value from annotated controller methods.
* For full page rendering with a single model and view, use {@link Rendering}.
*
* @author Rossen Stoyanchev
* @since 6.2
@ -63,59 +65,107 @@ public interface FragmentsRendering { @@ -63,59 +65,107 @@ public interface FragmentsRendering {
/**
* Create a builder and add a fragment with a view name and a model.
* Create a builder with one HTML fragment, also inheriting attributes from
* the shared model for the request.
* @param viewName the name of the view for the fragment
* @param model attributes for the fragment in addition to model
* attributes inherited from the model for the request
* @return this builder
* @since 6.2.13
*/
static Builder with(String viewName, Map<String, Object> model) {
return withCollection(List.of(Fragment.create(viewName, model)));
static Builder fragment(String viewName) {
return new DefaultFragmentsRenderingBuilder().fragment(viewName);
}
/**
* Variant of {@link #with(String, Map)} with a view name only, but also
* inheriting model attributes from the shared model for the request.
* @param viewName the name of the view for the fragment
* Create a builder with one HTML fragment.
* @param viewName the view name for the fragment
* @param model attributes for the fragment, in addition to attributes from the
* shared model for the request
* @return this builder
* @since 6.2.13
*/
static Builder with(String viewName) {
return withCollection(List.of(Fragment.create(viewName)));
static Builder fragment(String viewName, Map<String, Object> model) {
return new DefaultFragmentsRenderingBuilder().fragment(viewName, model);
}
/**
* Variant of {@link #with(String, Map)} with a collection of fragments.
* @param fragments the fragments to add; each fragment also inherits model
* Create a builder with multiple HTML fragments.
* @param fragments the fragments to add; each fragment also inherits
* attributes from the shared model for the request
* @return the created builder
* @since 6.2.13
*/
static Builder withCollection(Collection<Fragment> fragments) {
return new DefaultFragmentsRenderingBuilder(fragments);
static Builder fragments(Collection<Fragment> fragments) {
return new DefaultFragmentsRenderingBuilder().fragments(fragments);
}
/**
* Variant of {@link #with(String, Map)} with a {@link Publisher} of fragments.
* Create a builder with a {@link Publisher} of fragments.
* @param fragmentsPublisher the fragments to add; each fragment also
* inherits model attributes from the shared model for the request
* @return the created builder
* @since 6.2.13
*/
static <P extends Publisher<Fragment>> Builder withPublisher(P fragmentsPublisher) {
static <P extends Publisher<Fragment>> Builder fragmentsPublisher(P fragmentsPublisher) {
return new DefaultFragmentsRenderingBuilder(fragmentsPublisher);
}
/**
* Variant of {@link #withPublisher(Publisher)} that allows using any
* Variant of {@link #fragmentsPublisher(Publisher)} that allows using any
* producer that can be resolved to {@link Publisher} via
* {@link ReactiveAdapterRegistry}.
* @since 6.2.13
*/
static Builder withProducer(Object fragmentsProducer) {
return new DefaultFragmentsRenderingBuilder(adaptProducer(fragmentsProducer));
static Builder fragmentsProducer(Object fragmentsProducer) {
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(fragmentsProducer.getClass());
Assert.isTrue(adapter != null, "Unknown producer " + fragmentsProducer.getClass());
Publisher<Fragment> publisher = adapter.toPublisher(fragmentsProducer);
return fragmentsPublisher(publisher);
}
/**
* The same as {@link #fragment(String, Map)}.
* @deprecated in favor of {@link #fragment(String, Map)}
*/
@Deprecated(since = "6.2.13", forRemoval = true)
static Builder with(String viewName, Map<String, Object> model) {
return fragment(viewName, model);
}
private static Publisher<Fragment> adaptProducer(Object producer) {
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(producer.getClass());
Assert.isTrue(adapter != null, "Unknown producer " + producer.getClass());
return adapter.toPublisher(producer);
/**
* The same as {@link #fragments(Collection)}.
* @deprecated in favor of {@link #fragments(Collection)}
*/
@Deprecated(since = "6.2.13", forRemoval = true)
static Builder with(String viewName) {
return fragment(viewName);
}
/**
* The same as {@link #fragments(Collection)}.
* @deprecated in favor of {@link #fragments(Collection)}
*/
@Deprecated(since = "6.2.13", forRemoval = true)
static Builder withCollection(Collection<Fragment> fragments) {
return fragments(fragments);
}
/**
* The same as {@link #fragmentsPublisher(Publisher)}.
* @deprecated in favor of {@link #fragmentsPublisher(Publisher)}
*/
@Deprecated(since = "6.2.13", forRemoval = true)
static <P extends Publisher<Fragment>> Builder withPublisher(P fragmentsPublisher) {
return fragmentsPublisher(fragmentsPublisher);
}
/**
* The same as {@link #fragmentsProducer(Object)}.
* @deprecated in favor of {@link #fragmentsProducer(Object)}
*/
@Deprecated(since = "6.2.13", forRemoval = true)
static Builder withProducer(Object fragmentsProducer) {
return fragmentsProducer(fragmentsProducer);
}
@ -148,30 +198,39 @@ public interface FragmentsRendering { @@ -148,30 +198,39 @@ public interface FragmentsRendering {
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
* @param model attributes for the fragment in addition to model
* attributes inherited from the model for the request
* Add an HTML fragment.
* @param viewName the view name for the fragment
* @param model fragment attributes in addition to attributes from the
* shared model for the request
* @return this builder
*/
Builder fragment(String viewName, Map<String, Object> model);
/**
* Variant of {@link #fragment(String, Map)} with a view name only, where
* the fragment model also inherits model attributes from the shared
* Add an HTML fragment. The fragment will use attributes from the shared
* model for the request.
* @param viewName the name of the view for the fragment
* @param viewName the view name for the fragment
* @return this builder
*/
Builder fragment(String viewName);
/**
* Variant of {@link #fragment(String, Map)} with a {@link Fragment}.
* @param fragment the fragment to add
* Add an HTML fragment.
* @param fragment the fragment to add; the fragment also inherits
* attributes from the shared model for the request
* @return this builder
*/
Builder fragment(Fragment fragment);
/**
* Add HTML fragments.
* @param fragments the fragments to add; each fragment also inherits
* attributes from the shared model for the request
* @return this builder
* @since 6.2.13
*/
Builder fragments(Collection<Fragment> fragments);
/**
* Build the {@link FragmentsRendering} instance.
*/

4
spring-webflux/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java

@ -220,7 +220,7 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport imp @@ -220,7 +220,7 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport imp
}
valueMono = (result.getReturnValue() != null ?
Mono.just(FragmentsRendering.withPublisher(adapter.toPublisher(result.getReturnValue())).build()) :
Mono.just(FragmentsRendering.fragmentsPublisher(adapter.toPublisher(result.getReturnValue())).build()) :
Mono.empty());
valueType = ResolvableType.forClass(FragmentsRendering.class);
@ -252,7 +252,7 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport imp @@ -252,7 +252,7 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport imp
}
if (Collection.class.isAssignableFrom(clazz)) {
returnValue = FragmentsRendering.withCollection((Collection<Fragment>) returnValue).build();
returnValue = FragmentsRendering.fragments((Collection<Fragment>) returnValue).build();
clazz = FragmentsRendering.class;
}

4
spring-webflux/src/test/java/org/springframework/web/reactive/result/view/FragmentViewResolutionResultHandlerTests.java

@ -66,9 +66,9 @@ public class FragmentViewResolutionResultHandlerTests { @@ -66,9 +66,9 @@ public class FragmentViewResolutionResultHandlerTests {
static Stream<Arguments> arguments() {
Flux<Fragment> fragmentFlux = Flux.just(fragment1, fragment2).subscribeOn(Schedulers.boundedElastic());
return Stream.of(
Arguments.of(FragmentsRendering.withPublisher(fragmentFlux).build(),
Arguments.of(FragmentsRendering.fragmentsPublisher(fragmentFlux).build(),
on(Handler.class).resolveReturnType(FragmentsRendering.class)),
Arguments.of(FragmentsRendering.withCollection(List.of(fragment1, fragment2)).build(),
Arguments.of(FragmentsRendering.fragments(List.of(fragment1, fragment2)).build(),
on(Handler.class).resolveReturnType(FragmentsRendering.class)),
Arguments.of(fragmentFlux,
on(Handler.class).resolveReturnType(Flux.class, Fragment.class)),

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

@ -92,7 +92,7 @@ public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturn @@ -92,7 +92,7 @@ public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturn
}
if (returnValue instanceof Collection<?> mavs) {
returnValue = FragmentsRendering.with((Collection<ModelAndView>) mavs).build();
returnValue = FragmentsRendering.fragments((Collection<ModelAndView>) mavs).build();
}
if (returnValue instanceof FragmentsRendering rendering) {

2
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java

@ -398,7 +398,7 @@ public class ResponseBodyEmitterReturnValueHandler implements HandlerMethodRetur @@ -398,7 +398,7 @@ public class ResponseBodyEmitterReturnValueHandler implements HandlerMethodRetur
FragmentHttpServletResponse fragmentResponse =
new FragmentHttpServletResponse(this.response, this.charset);
FragmentsRendering render = FragmentsRendering.with(List.of(modelAndView)).build();
FragmentsRendering render = FragmentsRendering.fragments(List.of(modelAndView)).build();
render.resolveNestedViews(this::resolveViewName, this.locale);
render.render(modelAndView.getModel(), this.request, fragmentResponse);

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

@ -27,11 +27,15 @@ import org.springframework.web.servlet.ModelAndView; @@ -27,11 +27,15 @@ import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.SmartView;
/**
* Public API for HTML rendering of a collection of fragments each with a view
* and independent model. For use with frontends technologies such as
* Public API to render HTML fragments. A fragment is a portion of an HTML page.
* Normally HTML is rendered with a single model and view. This API allows
* using multiple model and view pairs, one for each HTML fragment.
*
* <p>For use with frontends technologies such as
* <a href="https://htmx.org/">htmx</a> where multiple page fragments may be
* rendered in one response. Supported as a return value from Spring MVC
* controller methods.
* rendered in one response.
*
* <p>Supported as a return value from controller methods.
*
* @author Rossen Stoyanchev
* @since 6.2
@ -51,37 +55,68 @@ public interface FragmentsRendering extends SmartView { @@ -51,37 +55,68 @@ public interface FragmentsRendering extends SmartView {
/**
* Create a builder and add a fragment with a view name and a model.
* Create a builder with one HTML fragment, also inheriting attributes from
* the shared model for the request.
* @param viewName the name of the view for the fragment
* @param model attributes for the fragment in addition to model
* attributes inherited from the shared model for the request
* @return the created builder
* @since 6.2.13
*/
static Builder with(String viewName, Map<String, Object> model) {
return new DefaultFragmentsRenderingBuilder().fragment(viewName, model);
static Builder fragment(String viewName) {
return new DefaultFragmentsRenderingBuilder().fragment(viewName);
}
/**
* Variant of {@link #with(String, Map)} with a view name only, but also
* inheriting model attributes from the shared model for the request.
* @param viewName the name of the view for the fragment
* Create a builder with one HTML fragment.
* @param viewName the view name for the fragment
* @param model attributes for the fragment, in addition to attributes from the
* shared model for the request
* @return the created builder
* @since 6.2.13
*/
static Builder with(String viewName) {
return new DefaultFragmentsRenderingBuilder().fragment(viewName);
static Builder fragment(String viewName, Map<String, Object> model) {
return new DefaultFragmentsRenderingBuilder().fragment(viewName, model);
}
/**
* Variant of {@link #with(String, Map)} with a collection of fragments.
* @param fragments the fragments to add; each fragment also inherits model
* Create a builder with multiple HTML fragments.
* @param fragments the fragments to add; each fragment also inherits
* attributes from the shared model for the request
* @return the created builder
* @since 6.2.13
*/
static Builder with(Collection<ModelAndView> fragments) {
static Builder fragments(Collection<ModelAndView> fragments) {
return new DefaultFragmentsRenderingBuilder().fragments(fragments);
}
/**
* The same as {@link #fragment(String, Map)}.
* @deprecated in favor of {@link #fragment(String, Map)}
*/
@Deprecated(since = "6.2.13", forRemoval = true)
static Builder with(String viewName, Map<String, Object> model) {
return fragment(viewName, model);
}
/**
* The same as {@link #fragment(String)}.
* @deprecated in favor of {@link #fragment(String)}
*/
@Deprecated(since = "6.2.13", forRemoval = true)
static Builder with(String viewName) {
return fragment(viewName);
}
/**
* The same as {@link #fragments(Collection)}.
* @deprecated in favor of {@link #fragments(Collection)}
*/
@Deprecated(since = "6.2.13", forRemoval = true)
static Builder with(Collection<ModelAndView> fragments) {
return fragments(fragments);
}
/**
* Defines a builder for {@link FragmentsRendering}.
*/
@ -111,32 +146,32 @@ public interface FragmentsRendering extends SmartView { @@ -111,32 +146,32 @@ public interface FragmentsRendering extends SmartView {
Builder headers(Consumer<HttpHeaders> headersConsumer);
/**
* Add a fragment with a view name and a model.
* Add an HTML fragment.
* @param viewName the name of the view for the fragment
* @param model attributes for the fragment in addition to model
* attributes inherited from the shared model for the request
* @param model fragment attributes in addition to attributes from the
* shared model for the request
* @return this builder
*/
Builder fragment(String viewName, Map<String, Object> model);
/**
* Variant of {@link #fragment(String, Map)} with a view name only, but
* also inheriting model attributes from the shared model for the request.
* Add an HTML fragment. The fragment will use attributes from the shared
* model for the request.
* @param viewName the name of the view for the fragment
* @return this builder
*/
Builder fragment(String viewName);
/**
* Variant of {@link #fragment(String, Map)} with a {@link ModelAndView}.
* @param fragment the fragment to add; the fragment also inherits model
* Add an HTML fragment.
* @param fragment the fragment to add; the fragment also inherits
* attributes from the shared model for the request
* @return this builder
*/
Builder fragment(ModelAndView fragment);
/**
* Variant of {@link #fragment(String, Map)} with a collection of {@link ModelAndView}s.
* Add multiple HTML fragments.
* @param fragments the fragments to add; each fragment also inherits model
* attributes from the shared model for the request
* @return this builder

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

@ -91,7 +91,7 @@ class ModelAndViewMethodReturnValueHandlerTests { @@ -91,7 +91,7 @@ class ModelAndViewMethodReturnValueHandlerTests {
@Test
void handleFragmentsRendering() throws Exception {
FragmentsRendering rendering = FragmentsRendering.with("viewName").build();
FragmentsRendering rendering = FragmentsRendering.fragment("viewName").build();
handler.handleReturnValue(rendering, returnParamModelAndView, mavContainer, webRequest);
assertThat(mavContainer.getView()).isInstanceOf(SmartView.class);

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

@ -56,7 +56,7 @@ public class DefaultFragmentsRenderingTests { @@ -56,7 +56,7 @@ public class DefaultFragmentsRenderingTests {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
FragmentsRendering view = FragmentsRendering.with("fragment1", Map.of("foo", "Foo"))
FragmentsRendering view = FragmentsRendering.fragment("fragment1", Map.of("foo", "Foo"))
.fragment("fragment2", Map.of("bar", "Bar"))
.header("headerName", "headerValue")
.build();

Loading…
Cancel
Save