-
Notifications
You must be signed in to change notification settings - Fork 471
Spring HATEOAS and custom (vendor-specific) media-types #263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I think "application/*+hal+json" should be included in the list of supported media types on the halConverter. We worked around this problem by manually registering a message converter that handles both "application/hal+json" and "application/*+hal+json". The latter adds support for vendor specific information in your media type, e.g. "application/api-v2.1+hal+json". Another workaround could be to implement your own post processor that overrides the mediatypes on the halConverter. |
I was thinking of doing the same thing they're doing; basically adding the HAL mapper to my converters but using Another thing I was thinking of doing was having an abstract class that encapsulates all that logic, and then adding the the HAL mapper to my custom media-type converter. Does that make sense? |
So this is my workaround. I'm using Spring Boot, so I have an application configuration class. I imagine you could do something similar in a regular Spring application.
|
I have a better workaround. I realized I didn't really need those explicit converters since I wasn't doing anything different other than processing regular JSON or JSON+HAL. So I did this instead:
|
Thanks for the workaround @vivin The problem is: when I request "application/json" the representation will be HAL+JSON and not clean JSON. I modified your code to use in
This way we don't need to look for Maybe there is another workaround to handle JSON and HAL+JSON separately, please let me know if any. |
@DanailMinchev yes your solution is cleaner. Another thing: I realized that the appropriate wildcard should be Also regarding |
@vivin : |
I found a slightly cleaner way for spring boot. When you define message converters in spring boot, you cannot have multiple instances because if you try to add two of the same type, the new one will override the other. So that was why I decide to override the existing JSON converter by adding the HAL media-types as well. But I found a better way for spring boot to do the same thing you are doing in a regular spring app (i.e., separating out the HAL converter):
So here I just have a private class that extends the existing JSON converter, but which supports HAL media types. |
@vivin |
@DanailMinchev No problem! |
Another update. I'm not sure why I didn't notice this before, but my new workaround DOES NOT WORK. Yes, you end up getting the serializations for the links in JSON, but you don't get the links serialized per HAL even when you have The original solution (adding all media-types, including hal to
So what are the solutions? We could use Another option is to use my original workaround, but that means we can only use One more option is based on what I already have: create an extended version of I think a lot of this confusion stems from there not being clarity on what the acceptable wild-card type is for HAL. All HAL is JSON, but all JSON is not HAL. Also, even if So for the time being, I think the only thing that makes sense semantically is explicitly spelling out your exact custom media types and then instantiating a If anyone has any ideas, please let me know because there seems to be a lot of confusion surrounding this. This basically makes it very difficult (or at least, not very elegant) to create a true, Level 3 RESTful API. |
I've summarized my concerns in a StackOverflow question. |
@vivin, correct me if my interpretation of the discussion is wrong (I know it's late and you've probably fixed your original problem but the issue is still open): your requirement of custom media types seems to revolve about assigning a media type to a given domain entity such that, for example, an order would be a response with media type While I agree that supporting more media types and, particularly, removing the current mandatory usage of HAL, I'd like to challenge the notion that Level 3 in the Richardson REST Maturity Model implies usage of custom media types, especially to represent domain entities: the way I understand his QCon presentation, he describes custom media types as something that's somewhat tied to user agents (hope I'm using the term correctly) and the context of the data. What you seem to require is link semantics, meaning that a link of type "order" is a link to an object that contains order information. Sorry for the long comment and let me know if I totally missed the point. |
@kewne No worries! Yeah, I agree that Level 3 doesn't mandate custom media-types; it only mandates the use of semantically-appropriate media-types, in the sense that the user-agent has the ability to specify exactly what resource it wants in addition to the way the resource should be represented. So the content-type is basically I think you helped clarify things for me :). I was going about the whole thing wrong anyway because whether something uses HAL or not would (should) be documented in its media-type. So a media-type for a resource that has HAL links is necessarily different from one that does not (even for the same resource). HAL describes the linking semantics of the resource, so a representation of the same resource without HAL has a different meaning, in the sense that it may convey linking semantics in a completely different way or not at all. I think that was the root of my mistake - I assumed that the HAL portion changed the way the resource was represented when really what it does is provide additional semantics for the way the resource is linked to other resources - now that information could be represented in different ways like you said: as XML or JSON, or some other format. |
So, there is no simple way to register custom media type with HAL support? |
TBH, Spring HATEOAS seems to be abandoned most of the time. It gets so little attention from the contributors. Have to patch it up all over the place. 😞 |
So here's my current hack to solve the problem. It only makes sense if you only emit HAL and don't support other representations. First, I enable hypermedia support explicitly: @EnableHypermediaSupport(type = HAL) That fixes the problem in no time but introduces other problems like breaking a lot of nice support and configurability of Jackson. For instance, you can't configure stuff like the following in your spring:
jackson:
serialization:
write_dates_as_timestamps: false Now, to bring all those awesome features back, I have a configuration class that uses the default object mapper builder to configure the object mapper created by Spring HATEOAS: @Configuration
class JacksonConfig {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private Jackson2ObjectMapperBuilder objectMapperBuilder;
@Bean
ObjectMapper objectMapper() {
objectMapperBuilder.configure(objectMapper);
return objectMapper;
}
} This is the simplest hack I've seen so far. If someone comes up with a simpler one, I'll be glad to switch to that. |
All workarounds appear to be ugly. In our case, this was the least invasive option, by simply adding an additional supported media type to the existing message converter. No custom message converters, no overriding of object mappers. Just a simple self-contained class which can be removed if this issue gets resolved one day. @Component
public class HalMediaTypeEnabler {
private static final MediaType CUSTOM_MEDIA_TYPE = new MediaType("application", "*+hal+json");
private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;
@Autowired
HalMediaTypeEnabler(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
this.requestMappingHandlerAdapter = requestMappingHandlerAdapter;
}
@PostConstruct
public void enableVndHalJson() {
for (HttpMessageConverter<?> converter : requestMappingHandlerAdapter.getMessageConverters()) {
if (converter instanceof MappingJackson2HttpMessageConverter && converter.getSupportedMediaTypes().contains(HAL_JSON)) {
MappingJackson2HttpMessageConverter messageConverter = (MappingJackson2HttpMessageConverter) converter;
messageConverter.setSupportedMediaTypes(Arrays.asList(HAL_JSON, CUSTOM_MEDIA_TYPE));
}
}
}
} |
Any progress on this issue? |
I have spent quite a bit of time upgrading Spring Data REST to use the new Spring HATEAOS Affordances API. This means SDR being able to product not only HAL but HAL-FORMS and Collection+JSON documents. So some of these other issues have not received as much love and attention. |
Are there any updates on this issue? We're also using a custom media type that's based on Our concrete solution was to do it via public class RequestMappingHandlerAdapterPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(@NonNull Object bean, String beanName) {
if (bean instanceof RequestMappingHandlerAdapter) {
RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean;
for (HttpMessageConverter<?> messageConverter : adapter.getMessageConverters()) {
if (isHateoasEnabledMessageConverter(messageConverter)) {
configureMediaTypes((MappingJackson2HttpMessageConverter) messageConverter);
}
}
}
return bean;
}
private static boolean isHateoasEnabledMessageConverter(HttpMessageConverter<?> messageConverter) {
return (messageConverter instanceof MappingJackson2HttpMessageConverter)
&& messageConverter.getSupportedMediaTypes().contains(MediaTypes.HAL_JSON);
}
private static void configureMediaTypes(MappingJackson2HttpMessageConverter messageConverter) {
messageConverter.setSupportedMediaTypes(Collections.singletonList(MyMediaTypes.VND_JSON));
}
} |
I’m actually working on a new feature (https://github.com/spring-projects/spring-hateoas/tree/feature/new-mediatypes) to register your own media type. |
That great to hear @gregturn - is this expected to make it into |
@vpavic We're knocking 'em out as we can, so I'm not really sure. |
Superceded and resolved via #833. |
I'm using Spring HATEOAS with Spring Boot for a service. Originally we simply had the endpoints returning
application/json
. Links and everything were created properly by Spring HATEOAS, but I noticed that thelinks
property was an array of maps. Instead, of simply being a map where therel
s are keys. I was able to fix that by adding@EnableHypermediaSupport(type=EnableHypermediaSupport.HypermediaType.HAL)
.My next step was to support semantic media-types (Level 3 of Richardson Maturity Model). I was able to create custom media-type converters for my custom media types by extending
MappingJackson2HttpMessageConverter
, and then adding the appropriateproduces
andconsumes
values on my@RequestMapping
annotations.However, I noticed that now the links are back to the old format where they are an array of hashes. I imagine this is because the converters are extending
MappingJackson2HttpMessageConverter
. I looked at the source code for Spring HATEOAS and I see that the converter that handleapplication/hal+json
is being defined inHyperMediaSupportBeanDefinitionRegistrar
:How can I ensure that HAL-type links work with my custom media-types? Will I have to duplicate this code inside each of my converters?
The text was updated successfully, but these errors were encountered: