«1. Обзор

В этом руководстве мы собираемся проиллюстрировать широкий спектр операций, в которых можно использовать клиент Spring REST — RestTemplate — и использовать его правильно.

Для стороны API во всех примерах мы будем запускать службу RESTful отсюда.

2. Уведомление об устаревании

Начиная с Spring Framework 5, наряду со стеком WebFlux, Spring представил новый HTTP-клиент под названием WebClient.

WebClient — это современный HTTP-клиент, альтернативный RestTemplate. Он не только предоставляет традиционный синхронный API, но также поддерживает эффективный неблокирующий и асинхронный подход.

Тем не менее, если мы разрабатываем новые приложения или переносим старые, рекомендуется использовать WebClient. В будущем RestTemplate будет объявлен устаревшим в будущих версиях.

3. Используйте GET для получения ресурсов

3.1. Get Plain JSON

Давайте начнем с простого и поговорим о запросах GET, приведя краткий пример использования API getForEntity():

RestTemplate restTemplate = new RestTemplate();
String fooResourceUrl
  = "http://localhost:8080/spring-rest/foos";
ResponseEntity<String> response
  = restTemplate.getForEntity(fooResourceUrl + "/1", String.class);
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));

Обратите внимание, что у нас есть полный доступ к ответу HTTP, поэтому мы можем выполнять такие действия, как проверка код состояния, чтобы убедиться, что операция прошла успешно, или работать с фактическим телом ответа:

ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(response.getBody());
JsonNode name = root.path("name");
assertThat(name.asText(), notNullValue());

Здесь мы работаем с телом ответа как со стандартной строкой и используем Джексон (и структуру узла JSON, которую Джексон предоставляет) для проверки некоторых деталей.

3.2. Получение POJO вместо JSON

Мы также можем напрямую сопоставить ответ с DTO ресурса:

public class Foo implements Serializable {
    private long id;

    private String name;
    // standard getters and setters
}

Теперь мы можем просто использовать API getForObject в шаблоне:

Foo foo = restTemplate
  .getForObject(fooResourceUrl + "/1", Foo.class);
assertThat(foo.getName(), notNullValue());
assertThat(foo.getId(), is(1L));

4. Используйте HEAD для получения Заголовки

Давайте теперь кратко рассмотрим использование HEAD, прежде чем переходить к более распространенным методам.

Здесь мы будем использовать API headForHeaders():

HttpHeaders httpHeaders = restTemplate.headForHeaders(fooResourceUrl);
assertTrue(httpHeaders.getContentType().includes(MediaType.APPLICATION_JSON));

5. Используйте POST для создания ресурса

Чтобы создать новый ресурс в API, мы можем использовать его с пользой API postForLocation(), postForObject() или postForEntity().

Первый возвращает URI только что созданного Ресурса, а второй возвращает сам Ресурс.

5.1. API postForObject()

RestTemplate restTemplate = new RestTemplate();

HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar"));
Foo foo = restTemplate.postForObject(fooResourceUrl, request, Foo.class);
assertThat(foo, notNullValue());
assertThat(foo.getName(), is("bar"));

5.2. API postForLocation()

Аналогично, давайте посмотрим на операцию, которая вместо полного возврата Ресурса просто возвращает Местоположение только что созданного Ресурса:

HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar"));
URI location = restTemplate
  .postForLocation(fooResourceUrl, request);
assertThat(location, notNullValue());

5.3. API exchange()

Давайте посмотрим, как выполнить POST с более общим API обмена:

RestTemplate restTemplate = new RestTemplate();
HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar"));
ResponseEntity<Foo> response = restTemplate
  .exchange(fooResourceUrl, HttpMethod.POST, request, Foo.class);
 
assertThat(response.getStatusCode(), is(HttpStatus.CREATED));
 
Foo foo = response.getBody();
 
assertThat(foo, notNullValue());
assertThat(foo.getName(), is("bar"));

5.4. Отправка данных формы

Далее давайте посмотрим, как отправить форму с помощью метода POST.

Во-первых, нам нужно установить для заголовка Content-Type значение application/x-www-form-urlencoded.

Это гарантирует, что на сервер может быть отправлена ​​большая строка запроса, содержащая пары имя/значение, разделенные символом \u0026:

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

Мы можем обернуть переменные формы в LinkedMultiValueMap:

MultiValueMap<String, String> map= new LinkedMultiValueMap<>();
map.add("id", "1");

Далее , мы создаем запрос, используя экземпляр HttpEntity:

HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);

Наконец, мы можем подключиться к службе REST, вызвав restTemplate.postForEntity() на конечной точке: /foos/form

ResponseEntity<String> response = restTemplate.postForEntity(
  fooResourceUrl+"/form", request , String.class);
 
assertThat(response.getStatusCode(), is(HttpStatus.CREATED));

6. Используйте ОПЦИИ получить разрешенные операции

Далее мы кратко рассмотрим использование запроса OPTIONS и исследуем разрешенные операции для определенного URI с использованием этого типа запроса; API — optionsForAllow:

Set<HttpMethod> optionsForAllow = restTemplate.optionsForAllow(fooResourceUrl);
HttpMethod[] supportedMethods
  = {HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE};
assertTrue(optionsForAllow.containsAll(Arrays.asList(supportedMethods)));

7. Используйте PUT для обновления ресурса

Далее мы начнем рассматривать PUT и, более конкретно, API exchange() для этой операции, поскольку API template.put довольно просто.

7.1. Простой PUT с помощью exchange()

Мы начнем с простой операции PUT для API — и имейте в виду, что операция не возвращает тело клиенту:

Foo updatedInstance = new Foo("newName");
updatedInstance.setId(createResponse.getBody().getId());
String resourceUrl = 
  fooResourceUrl + '/' + createResponse.getBody().getId();
HttpEntity<Foo> requestUpdate = new HttpEntity<>(updatedInstance, headers);
template.exchange(resourceUrl, HttpMethod.PUT, requestUpdate, Void.class);

7.2. PUT с exchange() и обратным вызовом запроса

Далее мы собираемся использовать обратный вызов запроса для выполнения PUT.

Давайте удостоверимся, что мы подготовили обратный вызов, где мы можем установить все необходимые нам заголовки, а также тело запроса:

RequestCallback requestCallback(final Foo updatedInstance) {
    return clientHttpRequest -> {
        ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(clientHttpRequest.getBody(), updatedInstance);
        clientHttpRequest.getHeaders().add(
          HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        clientHttpRequest.getHeaders().add(
          HttpHeaders.AUTHORIZATION, "Basic " + getBase64EncodedLogPass());
    };
}

Затем мы создаем Ресурс с запросом POST:

ResponseEntity<Foo> response = restTemplate
  .exchange(fooResourceUrl, HttpMethod.POST, request, Foo.class);
assertThat(response.getStatusCode(), is(HttpStatus.CREATED));

Затем мы обновляем ресурс:

Foo updatedInstance = new Foo("newName");
updatedInstance.setId(response.getBody().getId());
String resourceUrl =fooResourceUrl + '/' + response.getBody().getId();
restTemplate.execute(
  resourceUrl, 
  HttpMethod.PUT, 
  requestCallback(updatedInstance), 
  clientHttpResponse -> null);

8. Используйте DELETE для удаления ресурса

Чтобы удалить существующий ресурс, мы быстро воспользуемся API delete():

String entityUrl = fooResourceUrl + "/" + existingResource.getId();
restTemplate.delete(entityUrl);

9 , Настройка тайм-аута

«Мы можем настроить RestTemplate на тайм-аут, просто используя ClientHttpRequestFactory:

RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

private ClientHttpRequestFactory getClientHttpRequestFactory() {
    int timeout = 5000;
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory
      = new HttpComponentsClientHttpRequestFactory();
    clientHttpRequestFactory.setConnectTimeout(timeout);
    return clientHttpRequestFactory;
}

И мы можем использовать HttpClient для дополнительных параметров конфигурации:

private ClientHttpRequestFactory getClientHttpRequestFactory() {
    int timeout = 5000;
    RequestConfig config = RequestConfig.custom()
      .setConnectTimeout(timeout)
      .setConnectionRequestTimeout(timeout)
      .setSocketTimeout(timeout)
      .build();
    CloseableHttpClient client = HttpClientBuilder
      .create()
      .setDefaultRequestConfig(config)
      .build();
    return new HttpComponentsClientHttpRequestFactory(client);
}

10. Заключение

В этой статье мы рассмотрели основной HTTP Глаголы, использующие RestTemplate для организации запросов, использующих все это.

Если вы хотите узнать, как выполнить аутентификацию с помощью шаблона, ознакомьтесь с нашей статьей о базовой аутентификации с помощью RestTemplate.

Реализацию всех этих примеров и фрагментов кода можно найти на GitHub.