Skip to content

Commit 17538c6

Browse files
committed
Enable rendering as HAL FORMS.
We're now registering an HttpMessageConverter to render requests for HAL FORMS using the HalFormsConfiguration present in the application. We're currently not adding any affordances yet. The change solely enables user code to add affordances explicitly, e.g. via RepresentationModelProcessor implementations. Fixes: #1991.
1 parent c89369b commit 17538c6

File tree

3 files changed

+52
-4
lines changed

3 files changed

+52
-4
lines changed

spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaWebTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.springframework.hateoas.Link;
4646
import org.springframework.hateoas.LinkRelation;
4747
import org.springframework.hateoas.Links;
48+
import org.springframework.hateoas.MediaTypes;
4849
import org.springframework.hateoas.server.LinkRelationProvider;
4950
import org.springframework.http.MediaType;
5051
import org.springframework.mock.web.MockHttpServletResponse;
@@ -731,6 +732,14 @@ public void createThenPutWithProjection() throws Exception {
731732
assertThat(content.read("$.calculatedName", String.class)).isEqualTo("calculated-put");
732733
}
733734

735+
@Test // #1991
736+
public void answersToHalFormsRequests() throws Exception {
737+
738+
mvc.perform(get("/")
739+
.accept(MediaTypes.HAL_FORMS_JSON))
740+
.andExpect(status().isOk());
741+
}
742+
734743
private List<Link> preparePersonResources(Person primary, Person... persons) throws Exception {
735744

736745
Link peopleLink = client.discoverUnique(LinkRelation.of("people"));

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.springframework.data.rest.webmvc.support.JpaHelper;
3535
import org.springframework.data.util.ProxyUtils;
3636
import org.springframework.data.util.Streamable;
37+
import org.springframework.hateoas.MediaTypes;
3738
import org.springframework.http.HttpMethod;
3839
import org.springframework.http.MediaType;
3940
import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor;
@@ -217,6 +218,7 @@ protected ProducesRequestCondition customize(ProducesRequestCondition condition)
217218
Set<String> mediaTypes = new LinkedHashSet<String>();
218219
mediaTypes.add(configuration.getDefaultMediaType().toString());
219220
mediaTypes.add(MediaType.APPLICATION_JSON_VALUE);
221+
mediaTypes.add(MediaTypes.HAL_FORMS_JSON_VALUE);
220222

221223
return new ProducesRequestCondition(mediaTypes.toArray(new String[mediaTypes.size()]));
222224
}

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@
104104
import org.springframework.hateoas.mediatype.hal.HalConfiguration;
105105
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule;
106106
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalHandlerInstantiator;
107+
import org.springframework.hateoas.mediatype.hal.forms.HalFormsConfiguration;
108+
import org.springframework.hateoas.mediatype.hal.forms.Jackson2HalFormsModule;
107109
import org.springframework.hateoas.server.LinkRelationProvider;
108110
import org.springframework.hateoas.server.core.EvoInflectorLinkRelationProvider;
109111
import org.springframework.hateoas.server.mvc.RepresentationModelProcessorInvoker;
@@ -143,7 +145,7 @@
143145
* @author Christoph Strobl
144146
*/
145147
@Configuration(proxyBeanMethods = false)
146-
@EnableHypermediaSupport(type = HypermediaType.HAL)
148+
@EnableHypermediaSupport(type = { HypermediaType.HAL, HypermediaType.HAL_FORMS })
147149
@ImportResource("classpath*:META-INF/spring-data-rest/**/*.xml")
148150
@Import({ RestControllerImportSelector.class, //
149151
SpringDataJacksonConfiguration.class, //
@@ -573,13 +575,45 @@ public TypeConstrainedMappingJackson2HttpMessageConverter halJacksonHttpMessageC
573575
return converter;
574576
}
575577

578+
/**
579+
* {@link HttpMessageConverter} to support rendering HAL FORMS.
580+
*
581+
* @param linkCollector
582+
* @return
583+
* @since 3.5
584+
*/
585+
@Bean
586+
TypeConstrainedMappingJackson2HttpMessageConverter halFormsJacksonHttpMessageConverter(LinkCollector linkCollector) {
587+
588+
LinkRelationProvider defaultedRelProvider = this.relProvider.getIfUnique(EvoInflectorLinkRelationProvider::new);
589+
HalFormsConfiguration configuration = new HalFormsConfiguration(
590+
halConfiguration.getIfUnique(() -> new HalConfiguration()));
591+
CurieProvider curieProvider = this.curieProvider
592+
.getIfUnique(() -> new DefaultCurieProvider(Collections.emptyMap()));
593+
ObjectMapper mapper = basicObjectMapper();
594+
595+
mapper.registerModule(persistentEntityJackson2Module(linkCollector));
596+
mapper.registerModule(new Jackson2HalFormsModule());
597+
mapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(
598+
defaultedRelProvider, curieProvider, resolver.getObject(), configuration.getHalConfiguration(),
599+
applicationContext.getAutowireCapableBeanFactory()));
600+
601+
TypeConstrainedMappingJackson2HttpMessageConverter converter = new TypeConstrainedMappingJackson2HttpMessageConverter(
602+
RepresentationModel.class);
603+
converter.setSupportedMediaTypes(Collections.singletonList(MediaTypes.HAL_FORMS_JSON));
604+
converter.setObjectMapper(mapper);
605+
606+
return converter;
607+
}
608+
576609
public ObjectMapper halObjectMapper(LinkCollector linkCollector) {
577610

578611
LinkRelationProvider defaultedRelProvider = this.relProvider.getIfUnique(EvoInflectorLinkRelationProvider::new);
579612
HalConfiguration halConfiguration = this.halConfiguration.getIfUnique(HalConfiguration::new);
580-
HalHandlerInstantiator instantiator = new HalHandlerInstantiator(defaultedRelProvider,
581-
curieProvider.getIfUnique(() -> new DefaultCurieProvider(Collections.emptyMap())), resolver.getObject(),
582-
halConfiguration, applicationContext.getAutowireCapableBeanFactory());
613+
CurieProvider curieProvider = this.curieProvider
614+
.getIfUnique(() -> new DefaultCurieProvider(Collections.emptyMap()));
615+
HalHandlerInstantiator instantiator = new HalHandlerInstantiator(defaultedRelProvider, curieProvider,
616+
resolver.getObject(), halConfiguration, applicationContext.getAutowireCapableBeanFactory());
583617

584618
ObjectMapper mapper = basicObjectMapper();
585619
mapper.registerModule(persistentEntityJackson2Module(linkCollector));
@@ -739,6 +773,7 @@ public RepositoryInvokerFactory repositoryInvokerFactory() {
739773
public List<HttpMessageConverter<?>> defaultMessageConverters(
740774
@Qualifier("jacksonHttpMessageConverter") TypeConstrainedMappingJackson2HttpMessageConverter jacksonHttpMessageConverter,
741775
@Qualifier("halJacksonHttpMessageConverter") TypeConstrainedMappingJackson2HttpMessageConverter halJacksonHttpMessageConverter,
776+
@Qualifier("halFormsJacksonHttpMessageConverter") TypeConstrainedMappingJackson2HttpMessageConverter halFormsJacksonHttpMessageConverter,
742777
AlpsJsonHttpMessageConverter alpsJsonHttpMessageConverter,
743778
UriListHttpMessageConverter uriListHttpMessageConverter, RepositoryRestConfigurerDelegate configurerDelegate,
744779
RepositoryRestConfiguration repositoryRestConfiguration) {
@@ -757,6 +792,8 @@ public List<HttpMessageConverter<?>> defaultMessageConverters(
757792
messageConverters.add(halJacksonHttpMessageConverter);
758793
}
759794

795+
messageConverters.add(halFormsJacksonHttpMessageConverter);
796+
760797
MappingJackson2HttpMessageConverter fallbackJsonConverter = new MappingJackson2HttpMessageConverter();
761798
fallbackJsonConverter.setObjectMapper(basicObjectMapper());
762799

0 commit comments

Comments
 (0)