RestTemplate Introduction

No post do blog de hoje teremos um olhar sobre o bem conhecido cliente de descanso Springs – o RestTemplate. O RestTemplate é a classe central dentro da estrutura Spring para executar pedidos HTTP síncronos no lado do cliente.

Like Spring JdbcTemplate, RestTemplate é também uma API de alto nível, que por sua vez é baseada num cliente HTTP. Por defeito, a classe java.net.HttpURLConnection do Java SDK é utilizada em RestTemplate. No entanto, o Spring Framework torna possível mudar facilmente para outro API cliente HTTP. Como fazer isto é descrito noutro post.

p>A maioria de nós tem certamente experiência com HttpURLConnection ou outra API cliente HTTP. Ao utilizá-la, notamos que para cada pedido é gerado repetidamente o mesmo código da placa de caldeira:

  • Criar um objecto URL e abrir a ligação
  • Configurar o pedido HTTP
  • Executar o pedido HTTP
  • Interpretação da resposta HTTP
  • Converter a resposta HTTP num objecto Java
  • Manipulação de excepções

Ao usar RestTemplate todas estas coisas acontecem em segundo plano e o programador não tem de se preocupar com isso.

Começando com a Primavera 5, o Cliente Web não bloqueador e reactivo oferece uma alternativa moderna a RestTemplateWebClient oferece suporte tanto para pedidos HTTP síncronos e assíncronos como para cenários de streaming. Portanto, RestTemplate será marcado como depreciado numa futura versão do Quadro de Primavera e não conterá quaisquer novas funcionalidades.

Configuração do projecto

Antes de começarmos realmente, gostaria de dar uma olhada mais atenta aos seguintes pontos da configuração do projecto:

  • dependências de utilização
  • classePOJO Employee
  • serviço web REST para testes

2.1 Dependências usadas

Para o projecto de RestTemplate precisamos das seguintes dependências na nossa aplicação baseada na Spring Boot:

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency></dependencies>

A Dependência spring-boot-starter-web é um iniciador para a construção de aplicações web. Esta dependência contém a classe RestTemplate, a opção de publicar serviços web REST e muitas outras coisas relacionadas com a web.

Como API cliente HTTP utilizamos o Apache HttpComponents para os seguintes exemplos. Lombok gera por exemplo Getter e Setter e ajuda-nos a evitar a repetição de código.

2.2 POJO Class Employee

A nossa classe POJO, que nos acompanhará através do exemplo, parece-se com isto:

@Data@NoArgsConstructor@AllArgsConstructorpublic class Employee { private long id; private String firstName; private String lastName; private long yearlyIncome;}

Obrigado a Lombok e @Data Anotação que obtemos os métodos getter e setter gratuitamente. Além disso, @Data gera automaticamente os seguintes métodos:

  • equals()
  • hashCode()
  • toString()
  • Construtor com todos os campos que são anotados com @NonNull

@NoArgsConstructor gera um construtor sem parâmetros e @AllArgsConstructor gera um construtor com todos os parâmetros.

2.3 REST Web Service For Testing

Para melhor compreender os seguintes exemplos, o projecto de demonstração inclui um serviço web REST muito útil. O RestController correspondente é guru.springframework.resttemplate.web.EmployeeRestController. O código para o controlador é mantido muito simples e funcional.

O serviço web REST oferece a possibilidade de criar, ler, actualizar e apagar recursos de empregados e suporta os verbos HTTP GETPOSTPUT e DELETE. Assim que a aplicação é interrompida, todas as alterações feitas aos recursos são perdidas. O serviço Web está disponível no ponto final http://localhost:8080/rest/employees.

RestTemplate Methods

p> Antes de olharmos juntos para o primeiro código fonte, damos uma vista de olhos aos métodos da classe RestTemplate. A classe fornece mais de 50 métodos, a maioria deles são sobrecarregados várias vezes. A tabela seguinte dá uma visão geral aproximada:

Método Descrição
void delete Executa a DELETE solicita e não devolve nada.
ResponseEntity<T> exchange Executa um método HTTP especificado, tais como GET ou POST, e retorna um ResponseEntity que contém tanto o código de estado HTTP como o recurso como um objecto.
T execute Trabalhos semelhantes a exchange, mas espera um adicional RequestCallback e um ResultSetExtractor como parâmetros. Isto é útil, por exemplo, se criar frequentemente pedidos complexos ou se quiser processar respostas complexas.
ResponseEntity<T> getForEntity Executar um GET pedido e devolve um ResponseEntity que contém tanto o código de estado como o recurso como um objecto.
T getForObject Trabalhos semelhantes a getForEntity, mas devolve o recurso directamente.
HttpHeaders headForHeaders Executa a HEAD solicita e devolve todos os cabeçalhos HTTP para o URL especificado.
Set<HttpMethod> optionsForAllow Executa um OPTIONS pedido e utiliza o cabeçalho Allow para devolver quais os métodos HTTP permitidos sob o URL especificado.
T patchForObject Executar a PATCH solicitar e devolver a representação do recurso a partir da resposta. O JDK HttpURLConnection não suporta PATCH, mas Apache HttpComponents e outros suportam.
ResponseEntity<T> postForEntity Execute a POST pedido e devolve um ResponseEntity que contém o código de estado assim como o recurso como um objecto.
URI postForLocation Trabalha como postForEntity, mas retorna o cabeçalho Location da resposta, que indica sob qual URI o recurso recém-criado pode ser alcançado.
T postForObject Trabalha como postForEntity, mas devolve o recurso directamente.
void put Executa a PUT solicita e não devolve nada.

A maior parte dos métodos estão sobrecarregados de acordo com o seguinte esquema:

  • URL as String e parâmetros URL como VarArgs do tipo String
  • URL as String e parâmetros URL como Map<String, String>
  • URL as java.net.URI sem suporte para parâmetros URL

Cada método com um tipo de retorno espera um tipo de classe genérica como parâmetro para determinar o tipo de resposta.

Demonstrações de RESTTemplate

Os exemplos seguintes mostram como podemos consumir um serviço web REST utilizando a classe RestTemplate. Todos os exemplos seguintes estão no EmployeeRestClient class. É um cliente simples que envolve RestTemplate e fornece métodos relacionados com os Empregados. Como sempre, pode encontrar o código no nosso GitHub Repository.

public class EmployeeRestClient { private static final String RESOURCE_PATH = "/rest/employees"; private Logger LOG = LoggerFactory.getLogger(EmployeeRestClient.class); private String REQUEST_URI; private RestTemplate restTemplate; public EmployeeRestClient(RestTemplate restTemplate, String host, int port) { this.restTemplate = restTemplate; this.REQUEST_URI = host + ":" + port + RESOURCE_PATH; }}

até agora o EmployeeRestClient é bastante pouco espectacular. Recebemos um exemplo do RestTemplate do construtor. Também através dos parâmetros do construtor, obtemos o anfitrião e a porta em que o serviço web REST é executado.

Important: Todos os exemplos seguintes usam Apache HttpComponents como API cliente HTTP subjacente. Como isto pode ser configurado para o RestTemplate é explicado no post Using RestTemplate with Apaches HttpClient.

4.1 GET

4.1.1 getForEntity()

Comecemos com um exemplo simples para consultar um único recurso:

public ResponseEntity<Employee> getForEntity(long id) { ResponseEntity<Employee> entity = restTemplate.getForEntity(REQUEST_URI + "/{id}", Employee.class, Long.toString(id)); LOG.info("Status code value: " + entity.getStatusCodeValue()); LOG.info("HTTP Header 'ContentType': " + entity.getHeaders().getContentType()); return entity;}

Neste trecho de código, usamos o método getForEntity(), que retorna um objecto ResponseEntity como resultado. Como parâmetro, o método espera que o URI do recurso, incluindo quaisquer marcadores de lugar e o tipo de classe para converter o corpo.

ResponseEntity encapsula o código de estado da resposta HTTP, os cabeçalhos HTTP e o corpo que já foi convertido num objecto Java.

Em vez de consultar um único recurso, é claro que também é possível consultar uma colecção de recursos, como mostra o seguinte trecho de código:

public List<Employee> getAll(int page, int pageSize) { String requestUri = REQUEST_URI + "?page={page}&pageSize={pageSize}"; Map<String, String> urlParameters = new HashMap<>(); urlParameters.put("page", Integer.toString(page)); urlParameters.put("pageSize", Long.toString(pageSize)); ResponseEntity<Employee> entity = restTemplate.getForEntity(requestUri, Employee.class, urlParameters); return entity.getBody() != null? Arrays.asList(entity.getBody()) : Collections.emptyList();}

O serviço web REST espera um número de página e um pageSize (número de recursos por página) como parâmetros de consulta para consultar uma colecção de recursos. Para estes parâmetros, um Map é utilizado neste trecho de código em vez de VarArgs. O ResponseEntity é digitado num conjunto de Employee uma vez que esperamos um número indefinido de funcionários no resultado.

4.1.2 getForObject()

Se apenas o corpo for de interesse, o método getForObject() pode ser usado para consultar directamente o recurso como um objecto Java:

public Optional<Employee> getForObject(long id) { Employee employee = restTemplate.getForObject(REQUEST_URI + "/{id}", Employee.class, Long.toString(id)); return Optional.ofNullable(employee);}

No entanto, se quiser operar directamente na cadeia JSON, isto também é possível. Se o tipo de classe for simplesmente String.class, obtemos a string JSON em bruto:

public JsonNode getAsJsonNode(long id) throws IOException { String jsonString = restTemplate.getForObject(REQUEST_URI + "/{id}", String.class, id); ObjectMapper mapper = new ObjectMapper(); return mapper.readTree(jsonString);}

Via ObjectMapper podemos simplesmente transformar a cadeia JSON em JsonNode e depois aceder aos nós individuais do JsonNode muito confortavelmente através de jsonNode.path("fieldName"):

@Testvoid test_getAsJsonNode() throws Exception { JsonNode jsonNode = client.getAsJsonNode(3); assertNotNull(jsonNode); assertEquals(peterGrey.getId(), jsonNode.path("id").asLong()); assertEquals(peterGrey.getFirstName(), jsonNode.path("firstName").asText()); assertEquals(peterGrey.getLastName(), jsonNode.path("lastName").asText()); assertEquals(peterGrey.getYearlyIncome(), jsonNode.path("yearlyIncome").asLong());}

4.2 POST

4.2.1 postForObject()

A criação de um novo recurso via POST é possível com uma linha única:

public Employee postForObject(Employee newEmployee) { return restTemplate.postForObject(REQUEST_URI, newEmployee, Employee.class);}

Além do pedido URI, o método postForObject() espera qualquer objecto que represente o corpo do pedido e um tipo de classe para a conversão da resposta. Como resposta, o serviço web REST devolve o recurso criado incluindo o ID.

4.2.2 postForLocation()

Muito semelhante a postForObject funciona o método postForLocation(). Aqui só obtemos o URI do novo recurso em vez do recurso criado:

public URI postForLocation(Employee newEmployee) { return restTemplate.postForLocation(REQUEST_URI, newEmployee);}

4.2.3 postForEntity()

e finalmente há postForEntity, que devolve um ResponseEntity. Adicionalmente, o exemplo mostra-nos como podemos enviar os nossos próprios valores no cabeçalho HTTP para o servidor:

public ResponseEntity<Employee> postForEntity(Employee newEmployee) { MultiValueMap<String, String> headers = new HttpHeaders(); headers.add("User-Agent", "EmployeeRestClient demo class"); headers.add("Accept-Language", "en-US"); HttpEntity<Employee> entity = new HttpEntity<>(newEmployee, headers); return restTemplate.postForEntity(REQUEST_URI, entity, Employee.class);}

4.3 PUT

O método put() é utilizado para um HTTP PUT. O retorno do método é nulo. Podemos utilizar este método para actualizar um recurso de empregado:

public void put(Employee updatedEmployee) { restTemplate.put(REQUEST_URI + "/{id}", updatedEmployee, Long.toString(updatedEmployee.getId()));}

Contudo, há alguns casos de utilização em que gostaríamos de ter um ResponseEntity como resposta, uma vez que isto nos dá informação sobre o código de estado HTTP e os cabeçalhos HTTP enviados pelo servidor. Neste caso, podemos utilizar o método exchange():

public ResponseEntity<Employee> putWithExchange(Employee updatedEmployee) { return restTemplate.exchange(REQUEST_URI + "/{id}", HttpMethod.PUT, new HttpEntity<>(updatedEmployee), Employee.class, Long.toString(updatedEmployee.getId()));}

4.4 DELETE

O método delete() é utilizado para executar um DELETE pedido:

public void delete(long id) { restTemplate.delete(REQUEST_URI + "/{id}", Long.toString(id));}

Aqui novamente o mesmo que com put(). Se o código de estado HTTP ou os cabeçalhos HTTP forem de interesse, o método exchange() deve ser usado:

public ResponseEntity<Void> deleteWithExchange(long id) { return restTemplate.exchange(REQUEST_URI + "/{id}", HttpMethod.DELETE, null, Void.class, Long.toString(id));}

Desde que o servidor não nos devolva nada, usamos Void.class como o tipo para conversão de corpos de resposta.

4.5 HEAD

Se apenas os cabeçalhos HTTP de um pedido HTTP forem de interesse, usamos o método headForHeaders():

public HttpHeaders headForHeaders() { return restTemplate.headForHeaders(REQUEST_URI);}

Um teste deste método confirma que recebemos uma resposta com o tipo de conteúdo application/json quando consultamos o URL especificado:

@Testvoid test_headForHeaders() { HttpHeaders httpHeaders = client.headForHeaders(); assertNotNull(httpHeaders.getContentType()); assertTrue(httpHeaders.getContentType().includes(MediaType.APPLICATION_JSON));}

4.6 OPÇÕES

Com uma consulta via HTTP OPTIONS, podemos descobrir quais são os verbos HTTP permitidos para o URL especificado. RestTemplate fornece o método optionsForAllow() para isto:

public Set<HttpMethod> optionsForAllow(long id) { return restTemplate.optionsForAllow(REQUEST_URI + "/{id}", Long.toString(id));}

Um teste deste método confirma que podemos consultar o URL http://localhost:8080/rest/employees/1 com os verbos HTTP GETPUT e DELETE:

@Testvoid test_optionsForAllow() { Set<HttpMethod> httpMethods = client.optionsForAllow(1); List<HttpMethod> expectedHttpMethods = List.of(HttpMethod.GET, HttpMethod.PUT, HttpMethod.DELETE); assertTrue(httpMethods.containsAll(expectedHttpMethods));}

Sumário

Neste post de blogue, analisámos como trabalhamos com a classe RestTemplate. Analisámos o seguinte:

  • uma visão geral do RestTemplate e os seus métodos
  • umerosos exemplos de código para os seguintes verbos HTTP:
    • GET
    • POST
    • PUT
    • DELETE
    • HEAD
    • OPTIONS

Tambem, gostava de verificar o repositório do projecto no GitHub. Lá encontrará também uma aula de teste, que não discutimos em detalhe aqui.

I gostaria também de chamar a sua atenção para o post do blogue Using RestTemplate with Apaches HttpClient. Neste post, damos uma olhada em como configurar RestTemplate para o utilizar com a API cliente HTTP do Apache.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *