
Una volta definito il livello dei servizi, il passo successivo nello sviluppo di applicazioni con Spring Boot è quello di esporre queste funzionalità al mondo esterno tramite API REST. In altre parole, i servizi che abbiamo costruito diventano i “motori” della nostra applicazione, mentre le API REST costituiscono l’interfaccia attraverso la quale client esterni, applicazioni front-end o altri microservizi possono interagire con essi.
Le API RESTful rappresentano oggi lo standard più diffuso per la comunicazione tra sistemi distribuiti. Grazie alla loro semplicità e flessibilità, permettono di scambiare dati attraverso il protocollo HTTP utilizzando i metodi standard: GET, POST, PUT, PATCH, DELETE. In combinazione con Spring Boot, possiamo creare rapidamente endpoint robusti, sicuri e facili da manutenere, senza dover scrivere codice boilerplate complicato.
L’adozione di un’architettura RESTful consente inoltre di rispettare alcuni principi chiave:
-
Statelessness: ogni richiesta HTTP è indipendente, semplificando la scalabilità dell’applicazione.
-
Chiarezza nella struttura delle risorse: le URL rappresentano le risorse (ad esempio
/products
,/orders
) e non le azioni. -
Separazione chiara dei livelli: i controller gestiscono la comunicazione con il client, i servizi gestiscono la logica di business e i repository gestiscono l’accesso ai dati.
Seguendo questo approccio, otteniamo applicazioni più modulabili, testabili e facilmente estendibili, con una divisione dei ruoli chiara e coerente tra le componenti.
Creare un controller REST
Il controller REST è la classe responsabile di ricevere le richieste HTTP, delegare il lavoro al servizio corrispondente e restituire la risposta appropriata. In Spring Boot, la classe si annota con @RestController
, che è una combinazione di @Controller
e @ResponseBody
. Questo significa che tutti i metodi della classe restituiranno automaticamente dati serializzati in JSON, senza bisogno di configurazioni aggiuntive.
Insieme a @RestController
, possiamo utilizzare @RequestMapping
per definire il percorso base delle risorse gestite dal controller.
Esempio di base:
@RestController
@RequestMapping("/products")
public class ProductController {
private final ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping
public List<Product> getAllProducts() {
return productService.findAllProducts();
}
@GetMapping("/{id}")
public Product getProductById(@PathVariable Long id) {
return productService.findProductById(id);
}
@PostMapping
public Product createProduct(@RequestBody Product product) {
return productService.createProduct(product);
}
@PutMapping("/{id}")
public Product updateProduct(@PathVariable Long id, @RequestBody Product updated) {
return productService.updateProduct(id, updated);
}
@DeleteMapping("/{id}")
public void deleteProduct(@PathVariable Long id) {
productService.deleteProduct(id);
}
}
Analisi delle annotazioni principali
-
@RestController
: indica a Spring Boot che questa classe gestirà richieste REST e restituirà dati JSON. -
@RequestMapping("/products")
: definisce il percorso base per tutte le richieste a questo controller. -
@GetMapping
,@PostMapping
,@PutMapping
,@DeleteMapping
: mappano i metodi HTTP sui metodi della classe. -
@PathVariable
: consente di catturare valori dalla URL, ad esempio l’ID di una risorsa. -
@RequestBody
: indica che il corpo della richiesta HTTP deve essere deserializzato in un oggetto Java.
Gestione delle eccezioni e risposta coerente
In un’applicazione reale, non tutte le richieste avranno successo. È quindi fondamentale gestire correttamente le eccezioni e restituire messaggi chiari e coerenti al client. Spring Boot mette a disposizione il meccanismo di @ControllerAdvice
per centralizzare la gestione degli errori.
Esempio di gestione centralizzata:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ProductNotFoundException.class)
public ResponseEntity<String> handleProductNotFound(ProductNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleGeneralError(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Si è verificato un errore: " + ex.getMessage());
}
}
In questo modo, tutte le eccezioni sollevate dai servizi vengono intercettate e trasformate in risposte HTTP significative, migliorando l’esperienza del client e la manutenibilità del codice.
Gestione dei parametri e query string
Spesso le API REST devono ricevere parametri dinamici per filtrare, ordinare o cercare risorse. Spring Boot offre strumenti semplici per gestire questi scenari:
@GetMapping("/search")
public List<Product> searchProducts(@RequestParam String name, @RequestParam(required = false) Double maxPrice) {
return productService.searchByNameAndPrice(name, maxPrice);
}
-
@RequestParam
permette di leggere parametri dalla query string. -
Il parametro
required = false
indica che il parametro non è obbligatorio.
Questa gestione è essenziale per creare API flessibili e adatte a scenari complessi senza appesantire i controller.
Buone pratiche nella progettazione delle API REST
-
URL chiare e coerenti: usare nomi di risorse al plurale (
/products
,/orders
) e gerarchie logiche. -
Metodi HTTP coerenti: GET per leggere, POST per creare, PUT/PATCH per aggiornare, DELETE per rimuovere.
-
Status code significativi: 200 OK per successo, 201 Created per nuove risorse, 404 Not Found per risorse mancanti, 400 Bad Request per errori di input.
-
JSON coerente: risposte consistenti, evitando campi null o non necessari.
-
Validazione dei dati: usare
@Valid
insieme a Bean Validation (javax.validation
) per garantire integrità dei dati in ingresso.
Conclusione
Creare API REST in Spring Boot è un passo naturale dopo aver implementato il livello dei servizi. I controller REST, combinati con servizi ben strutturati e repository affidabili, costituiscono la base di applicazioni moderne, scalabili e facili da manutenere. La chiara separazione dei livelli consente di concentrarsi sulla logica di business senza compromettere la leggibilità, la testabilità e la qualità del codice.
Con una solida base di servizi e controller REST, sei pronto per affrontare step successivi come autenticazione e autorizzazione, documentazione delle API con Swagger/OpenAPI, e gestione avanzata delle query e dei filtri, rendendo la tua applicazione robusta e pronta per il mondo reale.