Статьи

Использование службы отдыха Spring-hateoas с использованием токенов типа Spring RestTemplate и Super

Spring-hateoas предоставляет приложениям отличный способ для создания сервисов на основе REST, которые следуют принципу HATEOAS .

Моя цель здесь состоит не в том, чтобы показать, как создать сам сервис, а в том, чтобы записать клиента в сервис.

Примером сервиса, который я собираюсь использовать, является « the spring-rest-stack », написанный Джошом Лонгом ( @starbuxman ). Конкретный подпроект, который я собираюсь использовать, это hateoas здесь . Если этот подпроект выполняется с помощью команды «mvn jetty», конечная точка на основе REST для отображения сведений о пользователе доступна по адресу http: // localhost: 8080 / users / 2, где «2» — это идентификатор пользователя и дает результат следующей структуры:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
{
    "links": [{
        "rel": "self",
        "href": "http://localhost:8080/users/2"
    }, {
        "rel": "customers",
        "href": "http://localhost:8080/users/2/customers"
    }, {
        "rel": "photo",
        "href": "http://localhost:8080/users/2/photo"
    }],
    "id": 2,
    "firstName": "Lois",
    "profilePhotoMediaType": null,
    "lastName": "Lane",
    "username": "loislane",
    "password": null,
    "profilePhotoImported": false,
    "enabled": true,
    "signupDate": 1370201631000
}

Чтобы связаться с конкретным клиентом этого пользователя, конечная точка находится по адресу http: // localhost: 8080 / users / 2 / Customers / 17, что дает следующую структуру:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
{
    "links": [{
        "rel": "self",
    }, {
        "rel": "user",
        "href": "http://localhost:8080/users/2"
    }],
    "id": 17,
    "signupDate": 1372461079000,
    "firstName": "Scott",
    "lastName": "Andrews",
    "databaseId": 17
}

Теперь для потребителя этих двух служб результат может быть представлен Java-типом под названием Resource в проекте Spring-hateoas и представляет собой универсальный класс со следующей сигнатурой:

01
02
03
04
05
06
07
08
09
10
11
public class Resource<T> extends ResourceSupport {
 
 protected Resource() {
  this.content = null;
 }
 
 public Resource(T content, Link... links) {
  this(content, Arrays.asList(links));
 }
 
...

Таким образом, потребитель двух вышеуказанных услуг получит следующие два типа:

1
2
3
Resource<User> user = .... //call to the service
 
Resource<Customer> customer = ... //call to the service

Теперь проблема в том, что, поскольку «user» и «customer» являются параметризованными типами, если бы я связывал типы, используя Джексона в качестве процессора json, я бы делал что-то вроде следующего:

1
2
3
ObjectMapper objectMapper = new ObjectMapper();
Resource<Customer> customer = objectMapper.readValue(customerAsJson, Resource.class);
Resource<User> user = objectMapper.readValue(userAsJson, Resource.class);

Выше не будет работать, однако, причина в том, что из-за потери информации о параметризованном Ресурсе из-за стирания типа Java, Джексон не будет знать, чтобы создать экземпляр Resource <User> или Resource <Customer>

Исправление заключается в использовании Super Type Token , который по сути является способом предоставления информации о типе для библиотек, таких как Джексон, и я уже писал об этом ранее здесь . При этом рабочий код для сопоставления json с соответствующим параметризованным типом будет выглядеть так:

1
2
3
ObjectMapper objectMapper = new ObjectMapper();
Resource<Customer> customer = objectMapper.readValue(customerAsJson, new TypeReference<Resource<Customer>>() {});
Resource<User> customer = objectMapper.readValue(userAsJson, new TypeReference<Resource<User>>() {});

Клиентская абстракция Spring для работы со службами на основе Rest — это RestTemplate , которая может работать с различными форматами сообщений (xml, json, atom и т. Д.), Используя абстракцию HttpMessageConverter для работы со спецификой связывания для каждого из форматов сообщений.

Spring RestTemplate предоставляет собственную реализацию токена Super Type, чтобы иметь возможность привязывать различные форматы сообщений к параметризованным типам, по аналогии с типом ссылки Джексона, он называется ParameterizedTypeReference .

ParameterizedTypeReference можно использовать для точного связывания ответов Rest для пользователей и клиентов с типами Java следующим образом:

1
2
3
4
5
6
7
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Resource<User>> responseEntity =
  restTemplate.exchange("http://localhost:8080/users/2", HttpMethod.GET, null, new ParameterizedTypeReference<Resource<User>>() {}, Collections.emptyMap());
if (responseEntity.getStatusCode() == HttpStatus.OK) {
 Resource<User> userResource = responseEntity.getBody();
 User user = userResource.getContent();
}
1
2
3
4
5
6
7
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Resource<Customer>> responseEntity =
  restTemplate.exchange("http://localhost:8080/users/2/customers/17", HttpMethod.GET, null, new ParameterizedTypeReference<Resource<Customer>>() {}, Collections.emptyMap());
if (responseEntity.getStatusCode() == HttpStatus.OK) {
 Resource<Customer> customerResource = responseEntity.getBody();
 Customer customer = customerResource.getContent();
}

В заключение, ParameterizedTypeReference предоставляет удобный способ работы с параметризованными типами и невероятно полезен при использовании REST-сервисов на основе Spring Hateoas.