I have two applications, one is called bar, what provides me resources in HAL format. The other is bcm to consume that service.
Example of response bar looks like this:
[
{
"name":"Brenner/in",
"_links":{
"self":{
"href":"..host/bbsng-app-rest/betrieb/15"
}
}
},
{
"name":"Dienstleistungshelfer/in HW",
"_links":{
"self":{
"href":"..host/bbsng-app-rest/betrieb/4"
}
}
},
{
...
Now I try to consume that from bcm using Spring RestTemplate. My Solution works, but I am not happy with that solution somehow and I guess there is a more clean way.
My Client-Code consuming RestService looks like:
@Autowired private RestTemplate template;
@Override
@SuppressWarnings("unchecked")
public BerufListe findeAlleBerufe() {
final BerufListe berufListe = new BerufListe();
final ResponseEntity<List> entity = template.getForEntity(LinkUtils.findBeruf(), List.class);
if (OK.equals(entity.getStatusCode())) {
final List<LinkedHashMap> body = entity.getBody();
for (final LinkedHashMap map : body) {
final LinkedHashMap idMap = (LinkedHashMap) map.get("_links");
String id = remove(String.valueOf(idMap.get("self")), "href=");
id = remove(id, "{");
id = remove(id, "}");
final String name = String.valueOf(map.get("name"));
final Beruf beruf = new Beruf(id, name);
berufListe.add(beruf);
}
}
return berufListe;
}
There are few ugly code as you see. One of them is, that I don't have any generics for my collections. The other point, I get the Resource_ID very complicated, and I use StringUtils.remove many times to extract the self url.
I am sure there must be a more convenient way to consume HAL-Response by Spring.
Thanks you.
Take a look the the Resource class from spring-hateaos.
It provides methods to extract the links from the response. However, as RestTemplate requires you to provide the class as variable, I have not found a different way other than creating a subclass of the desired entity and use it for RestTemplate.
You code could then look like this:
public class BerufResource extends Resource<Beruf> { }
BerufResource resource = template.getForEntity("http://example.at/berufe/1", BerufResource.class);
Beruf beruf = resource.getContent();
// do something with the entity
If you want to request a complete list, you would need to pass the array version of your entity to RestTemplate:
BerufResource[] resources = template.getForEntity("http://example.at/berufe", BerufResource[].class);
List<BerufResource> berufResources = Arrays.asList(resources);
for(BerufResource resource : berufResources) {
Beruf beruf = resource.getContent();
}
Unfortunately, we cannot write Resource<Beruf>.class
which defeats the whole purpose of the generic class, as we need to again create a subclass for every entity. The reason behind that is called type erasure. I've read somewhere that they are planning to introduce generic support for RestTemplate but I am not aware of any details.
Addressing the extraction of the id from the url:
I would recommend to use a different model on the client side and replace the type of the id field with string and store the whole url in it. This way, you can easily refetch the whole entity whenever you like and do not need to construct the URL yourself. You will need the URL later anyway, if you plan on submitting POST-requests to your API, as spring-hateaos requires you to send the link instead of the id. A typical POST-request could look like this:
{
"firstname": "Thomas",
"nachname": "Maier",
"profession": "http://example.at/professions/1"
}