Esistono diverse tecnologie per l’implementazione di un client RESTful in java, come ad esempio la RESTEasy Client API, ma la libreria RestTemplate disponibile nel modulo Spring Web è probabilmente quella più semplice da implementare. In questo post mostriamo un esempio completo, basato su Spring Boot, che implementa un insieme di microservizi consumati da un client. In particolare due miscorservizi che sono responsabili della generazione, rispettivamente, di un numero random intero ed di una operazione aritmetica da eseguire. Un terzo microservizio, invece, invoca i due precedenti per eseguire il calcolo l’operazione selezionata.
Per una rapida introduzione alla tecnologia Spring Boot si rimanda all’articolo Primi Passi con Spring Boot.
Il Progetto
Per semplicità strutturiamo il codice come un progetto maven multimodulo. Ciascun modulo è un progetto Spring Boot ed il progetto principale contiene tutte le dipendenze necessarie ai moduli.
Il pom.xml del progetto padre contiene quindi sia il riferimento a spring-boot-starter-parent
che la dipendenza a spring-boot-starter-web
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<project ...> <modelVersion>4.0.0</modelVersion> <groupId>it.javaboss</groupId> <artifactId>restconsumer</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <modules> <module>numbergenerator</module> <module>opgenerator</module> <module>calculator</module> </modules> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project> |
Random Generator
I due microservizi, responsabili della generazione dei numeri casuali e dell’operazione aritmetica sono, caratterizzati da due classi controller, quindi annotate con @RestController
, che implementano un unico metodo http GET il quale restituisce un json con le informazioni generate.
{ “value” : “-” } |
{ “value” : 93 } |
Il comportamento dei due servizi è parametrizzato attraverso due file di configurazione application.yml
in cui è anche indicata la porta in cui sono esposti. Questo perchè, trattandosi di applicazioni Spring Boot separate, ciascun servizio dovrà essere in ascolto su porte differenti.
|
|
Number Generator
Le classi caratteristiche del generatore di numeri casuali sono due: un coltroller ed una property holder.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@RestController public class NumberGeneratorController { Random rand = new Random(); @Autowired private NumberGeneratorProperties numberGeneratorProperties; @RequestMapping public ResponseEntity<RandomInteger> getNumber(){ RandomInteger value = new RandomInteger( rand.nextInt( numberGeneratorProperties.getMaxint() ) + 1 ); return new ResponseEntity<RandomInteger>( value, HttpStatus.OK ); } } |
1 2 3 4 5 6 |
@ConfigurationProperties public class NumberGeneratorProperties { private int maxint; // Getter and Setter } |
NumberGeneratorProperties
come un contenitore delle proprietà esternalizzate nel file application.yml
.
Operator Generator
In modo analogo anche il secondo microservizio è caratterizzato da un controller ed un property holder.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@RestController public class OperatorGeneratorController { Random rand = new Random(); @Autowired private OperatorGeneratorProperties operatorGeneratorProperties; @RequestMapping public ResponseEntity<RandomOperator> getOperator(){ int index = rand.nextInt( operatorGeneratorProperties.getOperators().size() ); RandomOperator operator = new RandomOperator( operatorGeneratorProperties.getOperators().get( index ) ); return new ResponseEntity<RandomOperator>( operator, HttpStatus.OK ); } } |
1 2 3 4 5 6 |
@ConfigurationProperties public class OperatorGeneratorProperties { private List<String> operators; // Getter and Setter } |
Consumo dei Servizi REST
Come riportato nel javadoc, RestTemplate
è il componente centrale di Spring per l’accesso in modo sincrono ai servizi HTTP. Esso semplifica la comunicazione con i server applicando i principi RESTful, gestendo le connessioni HTTP, e lasciando all’applicazione il solo compito di fornire l’URL al servizio, eventualmente con template variable, e l’estrazione dei risultati.
Il framework non fornisce un bean di tipo RestTemplate
ma un componente RestTemplateBuilder
che, secondo necessità, può essere iniettato ed utilizzato per ottenere un bean di tipo RestTemplate
da configurare secondo necessità. In generale è buona norma avere un bean differente per ciascun servizio da invocare anche perchè ogni servizio avrà caratteristiche specifiche.
Nel nostro caso per il progetto calculator
che invoca i microservizi numbergenerator
e operatorgenerato
abbiamo preferito utilizzare un unico bean RestTemplate generato nella classe CalculatorApplication
e per poi essere iniettato nei service all’occorrenza.
1 2 3 4 5 6 7 8 9 10 11 |
@SpringBootApplication public class CalculatorApplication { public static void main( String[] args ) throws Exception { SpringApplication.run(CalculatorApplication.class, args); } @Bean public RestTemplate rest() { return new RestTemplateBuilder().build(); } } |
L’applicazione include poi due service: NumberGeneratorService
e OperatorGeneraatorService,
che utilizzano il bean RestTemplate
per l’invocazione del relativo microservizio.
1 2 3 4 5 6 7 8 9 10 11 |
@Service public class NumberGeneratorService { private static final String URL = "http://localhost:8081"; @Autowired private RestTemplate rest; public Integer getNumber() { return rest.getForEntity(URL, RandomInteger.class).getBody().getValue(); } } |
1 2 3 4 5 6 7 8 9 10 11 |
@Service public class OperatorGeneraatorService { private static final String URL = "http://localhost:8082"; @Autowired private RestTemplate rest; public String getOperator() { return rest.getForEntity(URL, RandomOperator.class).getBody().getValue(); } } |
Come si nota sono molto simili e, come anticipato, l’uso del bean richiede esclusivamente di fornire l’url al servizio chiamato. Il metodo getForEntity
si occupa di eseguire la GET sull’URL specificato ed a convertire il json ottenuto nell bean indicato: RandomInteger
piuttosto che RandomOperator
.
I due service così implementati sono poi utilizzati da un CalculatorController il quale genera l’output utilizzando il parser di Spring per le espressioni SpEL.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@RestController public class CalculatorController { @Autowired OperatorGeneraatorService operatorGeneraatorService; @Autowired NumberGeneratorService numberGeneratorService; @RequestMapping public String calculate() { ExpressionParser parser = new SpelExpressionParser(); String expression = numberGeneratorService.getNumber() + " " + operatorGeneraatorService.getOperator() + " " + numberGeneratorService.getNumber(); Expression exp = parser.parseExpression( expression ); return expression + " = " + exp.getValue(); } } |
Esecuzione del Progetto
Per eseguire i tre progetti è sufficiente eseguire il task maven spring-boot:run
fornito dal plug-in spring-boot-maven-plugin
che abbiamo inserito nel pom.xml
del progetto principale. Si noti che il task va eseguito su tutti e tre i progetti e gli url su cui i servizi saranno esposti sono:
- http://localhost:8081/numbergenerator
- http://localhost:8082/operatorgenerator
- http://localhost:8080/calculator
Utilizzo di RestTemplate
Dopo aver mostrato questo semplice esempio vediamo alcuni aspetto “avanzati” nell’utilizzo di tale componente, A cominciare dai metodo che consentono di eseguire le diverse chiamate HTTP:
HTTP | RESTTEMPLATE |
---|---|
DELETE | delete(String, String...) |
GET | getForObject(String, Class, String...) |
HEAD | headForHeaders(String, String...) |
OPTIONS | optionsForAllow(String, String...) |
POST | postForLocation(String, Object, String...) |
PUT | put(String, Object, String...) |
Abbiamo poi parlato della possibilità di utilizzare l’URI Templating per la indicazione dell’URL del servizio invocato. Tutti i metodi sopra indicati ammettono come primo parametro l’URL che può contenere dei placeholder contenuti tra parentesi graffe. Il valore di tali parametri può essere indicato o attraverso un array di parametri nel metodo o mediante una mappa di stringhe.
Ad esempio se ne nostro servizio di generazione di un numero random avessimo avuto come parametro di input il range di valori richiesti, in termini di valore massimo e minimo, l’invocazione avrebbe potuto essere:
1 |
getForEntity("http://localhost:8081/numbergenerator/{minint}/{maxint}", RandomInteger.class, 10, 100); |
Infine gli oggetti passati e restituiti dai metodi getForObject()
, postForLocation()
e put()
sono convertiti in richieste HTTP e da risposte HTTP da specifici HttpMessageConverters
. I convertitori per i tipi MIME principali e i tipi Java sono registrati per impostazione predefinita, ma è anche possibile scrivere il proprio convertitore e collegarlo al RestTemplate.
Al sito della documentazione ufficiale https://spring.io/blog/2009/03/27/rest-in-spring-3-resttemplate è mostrato un esempio di convertitore per il recupero di immagini dal servizio Flickr.
Codice Sorgente
Il codice sorgente completo con tutti gli esempi presentati è scaricabile qui restconsumer.