«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.