Overview
W tym tutorialu zamierzamy zilustrować szeroki zakres operacji, w których klient Spring REST – RestTemplate – może być używany, i to używany dobrze.
Dla strony API wszystkich przykładów, będziemy uruchamiać usługę RESTful z tego miejsca.
Dalsza lektura:
Podstawowe uwierzytelnianie za pomocą RestTemplate
RestTemplate with Digest Authentication
Poznanie Spring Boot TestRestTemplate
Deprecation Notice
Począwszy od Spring Framework 5, wraz ze stosem WebFlux, Spring wprowadził nowego klienta HTTP o nazwie WebClient.
WebClient jest nowoczesnym, alternatywnym klientem HTTP do RestTemplate. Nie tylko dostarcza on tradycyjne synchroniczne API, ale także wspiera wydajne nieblokujące i asynchroniczne podejście.
Jeśli tworzymy nowe aplikacje lub migrujemy stare, dobrym pomysłem jest użycie WebClienta. Idąc dalej, RestTemplate zostanie zdeprecjonowany w przyszłych wersjach.
Użyj GET aby pobrać zasoby
3.1. Get Plain JSON
Zacznijmy od prostego omówienia żądań GET, z szybkim przykładem wykorzystującym 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));
Zauważ, że mamy pełny dostęp do odpowiedzi HTTP, więc możemy robić takie rzeczy jak sprawdzenie kodu statusu, aby upewnić się, że operacja się powiodła lub pracować z faktyczną treścią odpowiedzi:
ObjectMapper mapper = new ObjectMapper();JsonNode root = mapper.readTree(response.getBody());JsonNode name = root.path("name");assertThat(name.asText(), notNullValue());
Pracujemy tutaj z ciałem odpowiedzi jako standardowym Stringiem i używamy Jacksona (oraz struktury węzłów JSON, którą Jackson dostarcza) do weryfikacji niektórych szczegółów.
3.2. Pobieranie POJO zamiast JSON
Możemy również zmapować odpowiedź bezpośrednio do Resource DTO:
public class Foo implements Serializable { private long id; private String name; // standard getters and setters}
Teraz możemy po prostu użyć API getForObject w szablonie:
Foo foo = restTemplate .getForObject(fooResourceUrl + "/1", Foo.class);assertThat(foo.getName(), notNullValue());assertThat(foo.getId(), is(1L));
Użyj HEAD do pobierania nagłówków
Przyjrzyjrzyjmy się teraz szybko użyciu HEAD zanim przejdziemy do bardziej powszechnych metod.
Będziemy tutaj używać API headForHeaders():
HttpHeaders httpHeaders = restTemplate.headForHeaders(fooResourceUrl);assertTrue(httpHeaders.getContentType().includes(MediaType.APPLICATION_JSON));
Użyj POST aby utworzyć Zasób
Aby utworzyć nowy Zasób w API, możemy zrobić dobry użytek z API postForLocation(), postForObject() lub postForEntity().
Pierwsze z nich zwraca URI nowo utworzonego Zasobu, podczas gdy drugie zwraca sam Zasób.
5.1. The postForObject() API
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()
Podobnie, przyjrzyjmy się operacji, która zamiast zwracać pełny Zasób, zwraca tylko Lokalizację tego nowo utworzonego Zasobu:
HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar"));URI location = restTemplate .postForLocation(fooResourceUrl, request);assertThat(location, notNullValue());
5.3. The exchange() API
Spójrzmy jak zrobić POST z bardziej ogólnym API wymiany:
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. Przesyłanie danych z formularza
Następnie przyjrzyjmy się, jak przesłać formularz metodą POST.
Po pierwsze, musimy ustawić nagłówek Content-Type na application/x-www-form-urlencoded.
Dzięki temu do serwera może zostać wysłany duży ciąg zapytania, zawierający pary nazwa/wartość oddzielone &:
HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
Możemy zawinąć zmienne formularza w LinkedMultiValueMap:
MultiValueMap<String, String> map= new LinkedMultiValueMap<>();map.add("id", "1");
Następnie budujemy Request używając instancji HttpEntity:
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
Na koniec możemy połączyć się z usługą REST wywołując restTemplate.postForEntity() na punkcie końcowym: /foos/form
ResponseEntity<String> response = restTemplate.postForEntity( fooResourceUrl+"/form", request , String.class); assertThat(response.getStatusCode(), is(HttpStatus.CREATED));
Use OPTIONS to Get Allowed Operations
Następnie przyjrzymy się szybko użyciu żądania OPTIONS i zbadaniu dozwolonych operacji na konkretnym URI przy użyciu tego rodzaju żądania; API to optionsForAllow:
Set<HttpMethod> optionsForAllow = restTemplate.optionsForAllow(fooResourceUrl);HttpMethod supportedMethods = {HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE};assertTrue(optionsForAllow.containsAll(Arrays.asList(supportedMethods)));
Use PUT to Update a Resource
Następnie zaczniemy przyglądać się PUT, a dokładniej API exchange() dla tej operacji, ponieważ template.put API jest całkiem proste.
7.1. Proste PUT z exchange()
Zaczniemy od prostej operacji PUT przeciwko API – i pamiętaj, że operacja nie zwraca ciała z powrotem do klienta:
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 With exchange() and a Request Callback
Następnie, użyjemy request callback do wykonania PUT.
Upewnijmy się, że przygotowaliśmy callback, gdzie możemy ustawić wszystkie nagłówki, których potrzebujemy, jak również ciało żądania:
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()); };}
Następnie, tworzymy zasób za pomocą żądania POST:
ResponseEntity<Foo> response = restTemplate .exchange(fooResourceUrl, HttpMethod.POST, request, Foo.class);assertThat(response.getStatusCode(), is(HttpStatus.CREATED));
A następnie aktualizujemy Zasób:
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);
Użyj DELETE aby usunąć Zasób
Aby usunąć istniejący Zasób, szybko użyjemy API delete():
String entityUrl = fooResourceUrl + "/" + existingResource.getId();restTemplate.delete(entityUrl);
Skonfiguruj Timeout
Możemy skonfigurować RestTemplate do przekroczenia limitu czasu poprzez proste użycie ClientHttpRequestFactory:
RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());private ClientHttpRequestFactory getClientHttpRequestFactory() { int timeout = 5000; HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(); clientHttpRequestFactory.setConnectTimeout(timeout); return clientHttpRequestFactory;}
I możemy użyć HttpClient dla dalszych opcji konfiguracyjnych:
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);}
Podsumowanie
W tym artykule, przeszliśmy przez główne czasowniki HTTP, używając RestTemplate do orkiestracji żądań używających ich wszystkich.
Jeśli chcesz dowiedzieć się, jak przeprowadzić uwierzytelnianie za pomocą szablonu, sprawdź nasz artykuł o Basic Auth with RestTemplate.
Wdrożenie wszystkich tych przykładów i fragmentów kodu można znaleźć na GitHubie.
Zacznij przygodę ze Spring 5 i Spring Boot 2, dzięki kursowi Learn Spring:
>> WYBIERZ KURS