3 级RESTful API 的功能自定义媒体类型application/vnd.service.entity.v1+json
,例如,。在我的例子中,我使用HAL在我的 JSON 中提供相关资源之间的链接。
我不清楚使用 HAL+JSON 的自定义媒体类型的正确格式。我目前拥有的,看起来像application/vnd.service.entity.v1.hal+json
。我最初选择了application/vnd.service.entity.v1+hal+json
,但+hal
后缀未注册,因此违反了 RFC6838 的第 4.2.8 节。
现在 Spring HATEOAS 支持开箱即用的 JSON 链接,但对于 HAL-JSON,您需要使用@EnableHypermediaSupport(type=EnableHypermediaSupport.HypermediaType.HAL)
. 就我而言,因为我使用的是 Spring Boot,所以我将它附加到我的初始化程序类(即,扩展类SpringBootServletInitializer
)。但是 Spring Boot 不会立即识别我的自定义媒体类型。因此,为此,我必须弄清楚如何让它知道它需要将 HAL 对象映射器用于表单的媒体类型application/vnd.service.entity.v1.hal+json
。
对于我的第一次尝试,我在 Spring Boot 初始化程序中添加了以下内容:
@Bean
public HttpMessageConverters customConverters() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Arrays.asList(
new MediaType("application", "json", Charset.defaultCharset()),
new MediaType("application", "*+json", Charset.defaultCharset()),
new MediaType("application", "hal+json"),
new MediaType("application", "*hal+json")
));
CurieProvider curieProvider = getCurieProvider(beanFactory);
RelProvider relProvider = beanFactory.getBean(DELEGATING_REL_PROVIDER_BEAN_NAME, RelProvider.class);
ObjectMapper halObjectMapper = beanFactory.getBean(HAL_OBJECT_MAPPER_BEAN_NAME, ObjectMapper.class);
halObjectMapper.registerModule(new Jackson2HalModule());
halObjectMapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, curieProvider));
converter.setObjectMapper(halObjectMapper);
return new HttpMessageConverters(converter);
}
这有效,我以正确的 HAL 格式获取链接。然而,这是巧合。这是因为最终被报告为“兼容”的实际媒体类型application/vnd.service.entity.v1.hal+json
是*+json
; 它不承认它反对application/*hal+json
(见后面的解释)。我不喜欢这个解决方案,因为它污染了现有的 JSON 转换器,担心 HAL。所以,我做了一个不同的解决方案,如下所示:
@Configuration
public class ApplicationConfiguration {
private static final String HAL_OBJECT_MAPPER_BEAN_NAME = "_halObjectMapper";
@Autowired
private BeanFactory beanFactory;
@Bean
public HttpMessageConverters customConverters() {
return new HttpMessageConverters(new HalMappingJackson2HttpMessageConverter());
}
private class HalMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
public HalMappingJackson2HttpMessageConverter() {
setSupportedMediaTypes(Arrays.asList(
new MediaType("application", "hal+json"),
new MediaType("application", "*hal+json")
));
ObjectMapper halObjectMapper = beanFactory.getBean(HAL_OBJECT_MAPPER_BEAN_NAME, ObjectMapper.class);
setObjectMapper(halObjectMapper);
}
}
}
该解决方案不起作用;我最终在我的 JSON 中获得了不符合 HAL 的链接。这是因为application/vnd.service.entity.v1.hal+json
不被. application/*hal+json
发生这种情况的原因是MimeType
,它检查媒体类型兼容性,只识别以*+
作为子类型的有效通配符媒体类型开头的媒体类型(例如,application/*+json
)。这就是第一个解决方案奏效的原因(巧合)。
所以这里有两个问题:
MimeType
永远不会识别application/vnd.service.entity.v1.hal+json
针对application/*hal+json
.MimeType
将application/vnd.service.entity.v1+hal+json
识别针对的表单的供应商特定 HAL 媒体类型application/*+hal+json
,但是根据 RFC6838 的第 4.2.8 节,这些是无效的 mimetypes 。
似乎唯一正确的方法是 if+hal
被识别为有效后缀,在这种情况下,上面的第二个选项就可以了。否则,任何其他类型的通配符媒体类型都无法专门识别供应商特定的 HAL 媒体类型。唯一的选择是用 HAL 问题覆盖现有的 JSON 消息转换器(参见第一个解决方案)。
现在的另一个解决方法是在为消息转换器创建支持的媒体类型列表时指定您正在使用的每个自定义媒体类型。那是:
@Configuration
public class ApplicationConfiguration {
private static final String HAL_OBJECT_MAPPER_BEAN_NAME = "_halObjectMapper";
@Autowired
private BeanFactory beanFactory;
@Bean
public HttpMessageConverters customConverters() {
return new HttpMessageConverters(new HalMappingJackson2HttpMessageConverter());
}
private class HalMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
public HalMappingJackson2HttpMessageConverter() {
setSupportedMediaTypes(Arrays.asList(
new MediaType("application", "hal+json"),
new MediaType("application", "vnd.service.entity.v1.hal+json"),
new MediaType("application", "vnd.service.another-entity.v1.hal+json"),
new MediaType("application", "vnd.service.one-more-entity.v1.hal+json")
));
ObjectMapper halObjectMapper = beanFactory.getBean(HAL_OBJECT_MAPPER_BEAN_NAME, ObjectMapper.class);
setObjectMapper(halObjectMapper);
}
}
}
这样做的好处是不会污染现有的 JSON 转换器,但似乎不够优雅。有谁知道正确的解决方案?我会完全错了吗?