Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
The different ways of accessing a REST HATEOAS resource created with Spring Data. Using a Spring RestTemplate.
import java.util.Arrays;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.hal.Jackson2HalModule;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
@Configuration
public class RestConfiguration {
@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
mapper.registerModule(new Jackson2HalModule());
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Arrays.asList(MediaTypes.HAL_JSON));
converter.setObjectMapper(mapper);
return builder.messageConverters(converter).build();
}
}
import org.springframework.hateoas.Resources;
public class StudentResources extends Resources<Student> {
}
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.mvc.TypeReferences;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@Component
public class StudentsClient {
private static final String URL = "http://students-service/students?page={page}&size={size}";
@Autowired
RestTemplate template;
public Stream<Student> getStudents(int offset, int limit) {
Map<String, Integer> params = new HashMap<>();
params.put("page", offset / limit);
params.put("size", limit);
// Using external class
final ResponseEntity<StudentResources> studentResponse = template
.getForEntity(URL, StudentResources.class, params);
// Using instantiated ParametrizedTypeReference Resources
final ResponseEntity<Resources<Student>> studentResponse = template
.exchange(URL, HttpMethod.GET, null,
new ParameterizedTypeReference<Resources<Student>>() {
}, params);
// Using instantiated ParametrizedTypeReference Resources
final ResponseEntity<PagedResources<Student>> studentResponse = template
.exchange(URL, HttpMethod.GET, null,
new ParameterizedTypeReference<PagedResources<Student>>() {
}, params);
// Does not work for some reason, ends up with empty Resources inside Resources
// final ResponseEntity<Resources<Resource<Student>>> studentResponse = template
// .exchange(URL, HttpMethod.GET, null,
// new TypeReferences.ResourcesType<Resource<Student>>() {
// }, params);
// Using provided PagedResources type class, note the required {}
// This is used for return
final ResponseEntity<PagedResources<Resource<Student>>> studentResponse =
template
.exchange(URL, HttpMethod.GET, null,
new TypeReferences.PagedResourcesType<Resource<Student>>(){},
params);
return studentResponse.getBody().getContent().stream()
.map(Resource::getContent);
}
}
@tbholdren
Copy link

tbholdren commented Oct 9, 2018

Of all the examples I scoured the web for, this is the only one that actually helped me parse the HAL response. Kudos to you, sir.

@bartvanmiegem
Copy link

bartvanmiegem commented Mar 15, 2019

Big thanks for this! Your RestConfiguration made my code work. The _embedded field is gone and became content.

@vchambea
Copy link

vchambea commented May 5, 2019

Definitely, this is a clean, clear and simple example of what I was looking half day. Thank you for taking the time to document and share this.

@essare
Copy link

essare commented Dec 16, 2019

The clearest straightforward solution i was looking for
i really spent 2 days searching to achieve this.
Big thanks.

@grtlinux
Copy link

grtlinux commented Jun 2, 2020

WoW~~ Thank U for your sweet coding!! I found the solution of my terrible headache!!
Have a good year to you!

@krasowskir
Copy link

krasowskir commented Aug 30, 2020

works! Thanks a lot...
I also found this information: https://spring.io/blog/2020/04/22/spring-hateoas-brings-you-new-ways-to-configure-clients

autowire the hypermedia rest template configurer
@Autowired HypermediaRestTemplateConfigurer configurer

configure your rest template
RestTemplate restTemplate = configurer.registerHypermediaTypes(new RestTemplate())

use rest template
def result = restTemplate.exchange("...", HttpMethod.GET, new HttpEntity<Object>(new AddressDTO(...)), new ParameterizedTypeReference<CollectionModel<Address>>() {}, new HashMap<String, String>())

verify the result
result.body[0].lastName == 'testLastName'

@Kiolali
Copy link

Kiolali commented Nov 5, 2020

thank you so much! I was also searching for hours!

@Kiolali
Copy link

Kiolali commented Nov 16, 2020

works! Thanks a lot...
I also found this information: https://spring.io/blog/2020/04/22/spring-hateoas-brings-you-new-ways-to-configure-clients

autowire the hypermedia rest template configurer
@Autowired HypermediaRestTemplateConfigurer configurer

configure your rest template
RestTemplate restTemplate = configurer.registerHypermediaTypes(new RestTemplate())

use rest template
def result = restTemplate.exchange("...", HttpMethod.GET, new HttpEntity<Object>(new AddressDTO(...)), new ParameterizedTypeReference<CollectionModel<Address>>() {}, new HashMap<String, String>())

verify the result
result.body[0].lastName == 'testLastName'

I'm struggling now with List of Objects (simple Objects work!) that's why I wanted to change my Code to use the suggestion in the blog. I get the error "Could not autowire. No beans of 'HypermediaRestTemplateConfigurer' type found.". If I write the following:

    @Bean
    RestTemplateCustomizer restTemplateCustomizer(@Autowired
            HypermediaRestTemplateConfigurer configurer) {
        return restTemplate -> {
            configurer.registerHypermediaTypes(restTemplate);
        };
    }

the problem isn't solved. Do you have an idea what I'm doing wrong?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment