Simplify code that makes hal the default json media type

See gh-25546
pull/25571/head
Andy Wilkinson 4 years ago
parent 5c34db96bd
commit 5863edfdda

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,18 +22,21 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration; import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.client.LinkDiscoverers; import org.springframework.hateoas.client.LinkDiscoverers;
import org.springframework.hateoas.config.EnableHypermediaSupport; import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.hateoas.mediatype.hal.HalConfiguration;
import org.springframework.http.MediaType;
import org.springframework.plugin.core.Plugin; import org.springframework.plugin.core.Plugin;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
@ -53,9 +56,17 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class, @AutoConfigureAfter({ WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class }) HttpMessageConvertersAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class })
@EnableConfigurationProperties(HateoasProperties.class) @EnableConfigurationProperties(HateoasProperties.class)
@Import(HypermediaHttpMessageConverterConfiguration.class)
public class HypermediaAutoConfiguration { public class HypermediaAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "com.fasterxml.jackson.databind.ObjectMapper")
@ConditionalOnProperty(prefix = "spring.hateoas", name = "use-hal-as-default-json-media-type",
matchIfMissing = true)
HalConfiguration applicationJsonHalConfiguration() {
return new HalConfiguration().withMediaType(MediaType.APPLICATION_JSON);
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(LinkDiscoverers.class) @ConditionalOnMissingBean(LinkDiscoverers.class)
@ConditionalOnClass(ObjectMapper.class) @ConditionalOnClass(ObjectMapper.class)

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -29,6 +29,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.mediatype.hal.HalConfiguration;
import org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter; import org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter; import org.springframework.http.converter.AbstractHttpMessageConverter;
@ -41,7 +42,9 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
* *
* @author Andy Wilkinson * @author Andy Wilkinson
* @since 1.3.0 * @since 1.3.0
* @deprecated since 2.5.0 in favor of a {@link HalConfiguration} bean
*/ */
@Deprecated
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
public class HypermediaHttpMessageConverterConfiguration { public class HypermediaHttpMessageConverterConfiguration {

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -29,15 +29,16 @@ import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.client.LinkDiscoverer; import org.springframework.hateoas.client.LinkDiscoverer;
import org.springframework.hateoas.client.LinkDiscoverers; import org.springframework.hateoas.client.LinkDiscoverers;
import org.springframework.hateoas.config.EnableHypermediaSupport; import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.hateoas.mediatype.hal.HalLinkDiscoverer; import org.springframework.hateoas.mediatype.hal.HalLinkDiscoverer;
import org.springframework.hateoas.server.EntityLinks; import org.springframework.hateoas.server.EntityLinks;
import org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -87,28 +88,28 @@ class HypermediaAutoConfigurationTests {
} }
@Test @Test
void supportedMediaTypesOfTypeConstrainedConvertersIsCustomized() { void whenUsingTheDefaultConfigurationThenMappingJacksonConverterCanWriteHateoasTypeAsApplicationJson() {
this.contextRunner.run((context) -> { this.contextRunner.run((context) -> {
RequestMappingHandlerAdapter handlerAdapter = context.getBean(RequestMappingHandlerAdapter.class); RequestMappingHandlerAdapter handlerAdapter = context.getBean(RequestMappingHandlerAdapter.class);
for (HttpMessageConverter<?> converter : handlerAdapter.getMessageConverters()) { Optional<HttpMessageConverter<?>> mappingJacksonConverter = handlerAdapter.getMessageConverters().stream()
if (converter instanceof TypeConstrainedMappingJackson2HttpMessageConverter) { .filter(MappingJackson2HttpMessageConverter.class::isInstance).findFirst();
assertThat(converter.getSupportedMediaTypes()).contains(MediaType.APPLICATION_JSON, assertThat(mappingJacksonConverter).isPresent().hasValueSatisfying(
MediaTypes.HAL_JSON); (converter) -> assertThat(converter.canWrite(RepresentationModel.class, MediaType.APPLICATION_JSON))
} .isTrue());
}
}); });
} }
@Test @Test
void customizationOfSupportedMediaTypesCanBeDisabled() { void whenHalIsNotTheDefaultJsonMediaTypeThenMappingJacksonConverterCannotWriteHateoasTypeAsApplicationJson() {
this.contextRunner.withPropertyValues("spring.hateoas.use-hal-as-default-json-media-type:false") this.contextRunner.withPropertyValues("spring.hateoas.use-hal-as-default-json-media-type:false")
.run((context) -> { .run((context) -> {
RequestMappingHandlerAdapter handlerAdapter = context.getBean(RequestMappingHandlerAdapter.class); RequestMappingHandlerAdapter handlerAdapter = context.getBean(RequestMappingHandlerAdapter.class);
for (HttpMessageConverter<?> converter : handlerAdapter.getMessageConverters()) { Optional<HttpMessageConverter<?>> mappingJacksonConverter = handlerAdapter.getMessageConverters()
if (converter instanceof TypeConstrainedMappingJackson2HttpMessageConverter) { .stream().filter(MappingJackson2HttpMessageConverter.class::isInstance).findFirst();
assertThat(converter.getSupportedMediaTypes()).containsExactly(MediaTypes.HAL_JSON); assertThat(mappingJacksonConverter).isPresent()
} .hasValueSatisfying((converter) -> assertThat(
} converter.canWrite(RepresentationModel.class, MediaType.APPLICATION_JSON))
.isFalse());
}); });
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,8 @@
package smoketest.hateoas; package smoketest.hateoas;
import java.util.Arrays;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -38,8 +40,21 @@ class SampleHateoasApplicationTests {
private TestRestTemplate restTemplate; private TestRestTemplate restTemplate;
@Test @Test
void hasHalLinks() { void hasHalLinksWhenAnythingIsAcceptable() {
ResponseEntity<String> entity = this.restTemplate.getForEntity("/customers/1", String.class); HttpHeaders headers = new HttpHeaders();
ResponseEntity<String> entity = this.restTemplate.exchange("/customers/1", HttpMethod.GET,
new HttpEntity<>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).startsWith("{\"id\":1,\"firstName\":\"Oliver\",\"lastName\":\"Gierke\"");
assertThat(entity.getBody()).contains("_links\":{\"self\":{\"href\"");
}
@Test
void hasHalLinksWhenJsonIsAcceptable() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> entity = this.restTemplate.exchange("/customers/1", HttpMethod.GET,
new HttpEntity<>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).startsWith("{\"id\":1,\"firstName\":\"Oliver\",\"lastName\":\"Gierke\""); assertThat(entity.getBody()).startsWith("{\"id\":1,\"firstName\":\"Oliver\",\"lastName\":\"Gierke\"");
assertThat(entity.getBody()).contains("_links\":{\"self\":{\"href\""); assertThat(entity.getBody()).contains("_links\":{\"self\":{\"href\"");

Loading…
Cancel
Save