From fc49f3ead41d8c952115c03d41705f50e7faf2c9 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Sat, 2 Mar 2019 00:00:31 +0100 Subject: [PATCH] #837 - Added section on registering a custom media type. --- src/main/asciidoc/mediatypes.adoc | 87 ++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/src/main/asciidoc/mediatypes.adoc b/src/main/asciidoc/mediatypes.adoc index a644ae8f..12dadae3 100644 --- a/src/main/asciidoc/mediatypes.adoc +++ b/src/main/asciidoc/mediatypes.adoc @@ -56,4 +56,89 @@ Since the purpose of the `CurieProvider` API is to allow for automatic curie cre [[mediatypes.custom]] == Registering a custom media type -TODO +Spring HATEOAS allows to integrate support for custom media types through a set of SPIs, that third parties can implement. +The building blocks of an such an implementations are: + +1. Some form of Jackson ObjectMapper customization. In its most simple case that's a Jackson `Module` implementation. +2. A `LinkDiscoverer` implementation so that the client side support is able to detect links in representations generated. +3. Some configuration infrastructure that will allow Spring HATEOAS to find the custom implementation and pick up its configuration. + +[[mediatypes.custom.configuration]] +=== Custom media type configuration + +Custom media type implementations are picked up through Spring's `SpringFactories` mechanism, similar to the Java `ServiceLoader` API. +Each media type implementation needs to ship with a `spring.factories` in `META-INF` containing an implementation class entry for the `org.springframework.hateoas.config.MediaTypeConfigurationProvider` key: + +.An example `MediaTypeConfigurationProvider` declaration +==== +[source] +org.springframework.hateoas.config.MediaTypeConfigurationProvider=\ + com.acme.mymediatype.MyMediaTypeConfigurationProvider +==== + +That implementation class could then look as follows: + +.An example `MediaTypeConfigurationProvider` implementation in `META-INF/spring.factories` +==== +[source,java] +---- +class MyMediaTypeConfigurationProvider + implements MediaTypeConfigurationProvider { + + @Override + public Class getConfiguration() { + return MyMediaTypeConfiguration.class; <1> + } + + @Override + public boolean supportsAny(Collection mediaTypes) { + return mediaTypes.contains(MediaTypes.HAL_JSON); <2> + } +} +---- +The configuration class needs to have a default constructor and expose two methods: + +<1> A method returning a Spring configuration class that will be included in the application bootstrap when Spring HATEOAS is activated (either implicitly via Spring Boot auto-configuration or via `@EnableHypermediaSupport`). +<2> A callback method that will get the application selected media types to activate passed. This allows the media type implementation to control, when it it will be activated. +==== + +The configuration class has to implement `HypermediaMappingInformation`. It could look as simple as this: + +==== +[source, java] +---- +@Configuration +class MyMediaTypeConfiguration implements HypermediaMappingInformation { + + @Override + public List getMediaTypes() { + return MediaType.parse("application/vnd-acme-media-type") <1> + } + + @Override + public Module getJacksonModule() { + return new Jackson2MediaTypeModule(); <2> + } + + @Bean + MyLinkDiscoverer myLinkDiscoverer() { + return new MyLinkDiscoverer(); <3> + } +} +---- +<1> The configuration class returns the media type it wants to get Spring MVC / Spring WebFlux support set up. +<2> It overrides `getJacksonModule()` to provide custom serializers to create the media type specific representations. +<3> It also declares a custom `LinkDiscoverer` implementation for client side support. +==== + +The Jackson module usually declares `Serializer` and `Deserializer` implementations for the representation model types `RepresentationModel`, `EntityModel`, `CollectionModel` and `PagedModel`. +In case you need further customization of the Jackson `ObjectMapper` (like a custom `HandlerInstantiator`), you can alternatively override `configureObjectMapper(…)`. + +[[mediatypes.custom.recommendation]] +=== Recommendations + +The preferred way to implement media type representations is by providing a type hierarchy that matches the expected format and can be serialized by Jackson as is. +In the `Serializer` and `Deserializer` implementations registered for `RepresentationModel`, convert the instances into the media type specific model types and then lookup the Jackson serializer for those. + +The media types supported by default use the same configuration mechanism as third party implementations would. +So it's worth studying the implementations in https://github.com/spring-projects/spring-hateoas/tree/master/src/main/java/org/springframework/hateoas/mediatype[the `mediatype` package].