Skip to content

Instantly share code, notes, and snippets.

@ripla
Last active May 26, 2024 17:53
Show Gist options
  • Save ripla/6f1516e3d0c28f4d591303d4060342d4 to your computer and use it in GitHub Desktop.
Save ripla/6f1516e3d0c28f4d591303d4060342d4 to your computer and use it in GitHub Desktop.
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);
}
}
@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