![Creare servizi web RESTful](https://www.lateralecloud.it/public_html/wp-content/uploads/2018/10/sb_rest_controller.png)
In questo articolo imparerai a creare servizi web RESTful in Spring Boot. Vedrai come connettere l’applicazione ad un database e come implementare un’ambiente di persistenza definendo entità, creando servizi CRUD e controller, percorrendo i vari strati dell’applicazione.
Non farti spaventare da quello che hai appena letto, tra poco capirai di cosa si tratta… Continua a leggere.
Creare Servizi Web RESTful in Spring Boot
Senza entrare troppo nel merito delle definizioni (il web ne è pieno), cerchiamo di capire cosa si intende per Servizio Web RESTful.
I Web Service (servizi web) sono applicazioni software che, indipendentemente dal linguaggio di programmazione o piattaforma hardware, possono essere utilizzate da altre applicazioni attraverso l’interfaccia che espongono. L’interfaccia esposta dai Servizi Web permette di elaborare richieste (servizio) attraverso i protocolli del web come HTTP (da qui il nome Web Service).
Per un Web Service, REST è l’insieme dei principi per rappresentare la trasmissione delle risorse elaborate, tramite HTTP. Un principio fondamentale in REST è proprio l’esistenza di risorse alle quali si può accedere tramite un identificatore globale (un URI).
REST non è uno standard di per sé, ma le implementazioni RESTful utilizzano degli standard, come ad esempio HTTP, URI, JSON.
Per quanto detto, creare un Servizio Web RESTful significa adottare un approccio in cui le risorse sono gli elementi principali e sappiamo che l’effetto su una risorsa si basa su un metodo HTTP. Questo vuol dire che, una volta individuata la risorsa tramite URI, sappiamo quale operazione verrà svolta su di essa attraverso il metodo utilizzato nella chiamata HTTP.
Vediamo come tradurre tutto questo in applicazioni Spring Boot.
Servizi Web RESTful in Spring Boot
Nell’immagine seguente è rappresentato il modello architetturale di un Web Service Spring.
![Rappresentazione degli strati dell'architettura di un Web Service Spring](https://www.lateralecloud.it/public_html/wp-content/uploads/2018/10/spring-web-app-architecture.png)
Andando per analogie, nello schema:
- le risorse vengono rappresentate da un modello di dominio e rese pubbliche tramite oggetti DTO per essere trasferite.
- lo strato Web rappresenta l’interfaccia pubblica per l’identificazione delle risorse, implementata dai controller.
- lo strato Service definisce le operazioni (ad esempio CRUD) sulle risorse descritte nell’interfaccia, comunicando con uno strato Repository che accede ai dati.
Bootstrap di un'applicazione Spring Boot come Servizio Web RESTful
In un post precedente ho spiegato come creare un nuovo progetto Spring Boot in pochi minuti. Il risultato è un’applicazione semplice, ma che rappresenta la base per lo sviluppo di un’applicazione un pò più complessa come quella che vedrai tra poco.
Aggiungere le dipendenze
Spring Boot mette a disposizione un’insieme di librerie e configurazioni che facilitano lo sviluppo di servizi web tramite i pacchetti starter, reperibili attraverso le dipendenze Maven da aggiungere al file pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
e per l’utilizzo di della persistenza (JPA)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Configurare la connessione al database
Per memorizzare le risorse sulla quale eseguire operazioni (CRUD) si può utilizzare un database.
Nell’esempio che segue, è stato utilizzato il sistema di database relazionale PostgreSQL.
Volendo evitare ripetizioni tra gli articoli del sito e dato che la connessione al database non riguarda l'argomento principale di questo articolo, rimando alla lettura dell'articolo Comunicare con il database in un’applicazione Spring Boot per completare l'esempio.
Definire le risorse: modello di dominio
Le risorse rappresentano dati. Attraverso i servizi vogliamo eseguire operazioni CRUD sui dati presenti nel database.
Scegliendo Java, possiamo definire un tipo di dato facendo uso di JPA per gestirne la persistenza.
Come esempio, crea una classe Java nominata Team per la rappresentazione di un team di persone e la classe User, per rappresentare la persona stessa (utente).
@Entity
public class Team {
private Long id;
private String name;
private Set<User> members = new HashSet<>(0);
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column(nullable = false, unique = true)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToMany
public Set<User> getMembers() {
return members;
}
public void setMembers(Set<User> members) {
this.members = members;
}
}
@Entity
@Table(name = "app_user")
public class User {
private Long id;
private String name;
private String surname;
private String email;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
@Column(nullable = false, unique = true)
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Quelle appena viste sono le rappresentazioni dei tipi di dato app_user e team mappati nel database (non è l’unico modo per rappresentare un’entità).
Definire le operazioni CRUD sulle risorse: Service layer e Repository layer
Le operazioni CRUD (ma non solo) sui dati vengono gestite dai componenti Service attraverso i componenti Repository (o DAO). Spring Boot fornisce di per se le annotazioni necessarie al contesto.
In seguito, gli stessi servizi saranno esposti attraverso un’interfaccia per essere utilizzati esternamente (API, Web layer).
Repository layer
Lo strato dell’applicazione più vicino alle risorse prende il nome di Repository layer. Di questo livello fanno fanno parte quei componenti come i DAO (Data Access Object) che interagiscono direttamente con le risorse o i Repository. Un esempio di interfaccia Repository è il seguente:
@Repository
public interface TeamRepository extends JpaRepository<Team, Long> {
Optional<Team> findByName(String name);
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
L’interfaccia JpaRepository, presente nella libreria spring-boot-starter-data-jpa, fornisce le funzioni basilari per eseguire le operazioni CRUD sul dato, come il salvataggio, la cancellazione e il recupero del dato stesso.
Service layer
Lo strato Service funge da interfaccia tra gli strati Repository (privato) e Web (API, pubblico). Un componente Service viene utilizzato per la gestione e la trasformazione di un dato ottenuto dal database per la sua rappresentazione all’esterno, oltre all’implementazione della logica di business.
Un esempio è il seguente:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User getById(Long id) {
return this.userRepository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
}
public User create(UserDto dto) {
User user = new User();
user.setEmail(dto.getEmail());
user.setName(dto.getName());
user.setSurname(dto.getSurname());
return this.userRepository.save(user);
}
}
public class UserDto {
private String name;
private String surname;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
@Service
public class TeamService {
@Autowired
private TeamRepository teamRepository;
@Autowired
private UserRepository userRepository;
public Team getById(Long id) {
return this.teamRepository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Entity with id " + id + " not exists"));
}
public Team create(TeamDto dto) {
List<User> users = this.userRepository.findAllById(dto.getUsers());
Team team = new Team();
team.setName(dto.getName());
team.setMembers(new HashSet<>(users));
return this.teamRepository.save(team);
}
}
public class TeamDto {
private String name;
private Set<Long> users = new HashSet<>(0);
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Long> getUsers() {
return users;
}
public void setUsers(Set<Long> users) {
this.users = users;
}
}
Definire un'interfaccia pubblica per elaborare le richieste
L’ultimo passo per creare servizi web RESTful in Spring Boot, consiste nel definire un’interfaccia pubblica per le richieste esterne. Al termine, avrai definito le API nel modo corretto.
Web layer
A questo punto ci troviamo nello strato pubblico Web, o Web layer, di cui fanno parte i componenti definiti Controller, o meglio, RestController. Su questo, puoi trovare un articolo completo in questa pagina.
Brevemente, un componente RestController serve a definire le varie URI che identificano unicamente una risorsa e sulle quali sono assegnati i metodi HTTP adatti all’operazione da svolgere (ti ricordi quanto detto a proposito di REST?).
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("{id}")
public User getById(@PathVariable("id") Long id) {
return this.userService.getById(id);
}
@PostMapping
public User create(@RequestBody UserDto dto) {
return this.userService.create(dto);
}
}
@RestController
@RequestMapping("team")
public class TeamController {
@Autowired
private TeamService teamService;
@GetMapping("{id}")
public Team getById(@PathVariable("id") Long id) {
return this.teamService.getById(id);
}
@PostMapping
public Team create(@RequestBody TeamDto dto) {
return this.teamService.create(dto);
}
}
RestController come quelli visti permettono di elaborare richieste rispettivamente a partire dall’indirizzo /user e /team. Senza però definire le operazioni da eseguire sulla risorsa tramite metodi HTTP (post, get ecc..), il controller non sarà in grado di compiere nessuna attività. Utilizza le annotazioni @GetMapping e @PostMapping per completare l’esposizione dei servizi richiesti.
Nell’esempio:
- POST /user : riceve un oggetto come body della richiesta per aggiungere un User
- GET /user/1 : restituisce la risorsa User con id univoco 1 in formato JSON se questa è presente, 404 altrimenti
- POST /team : riceve un oggetto come body della richiesta per aggiungere un Team
- GET /team/1 : restituisce la risorsa Team con id univoco 1 in formato JSON se questa è presente, 404 altrimenti
Avvia l'applicazione e fai una prova, prima creando un Team, successivamente richiamando la risorsa. Utilizza Postman oppure i comandi cURL da terminale:
curl -X POST \
http://localhost:8080/user \
-H 'content-type: application/json' \
-d '{
"name": "test",
"surname": "test",
"email": "test@email.com"
}'
curl -X GET http://localhost:8080/user/1
Hai appena visto come creare servizi web RESTful con Spring Boot!
Prossimi passi?
A questo punto potrai realizzare in autonomia il tuo Web Service. Un ulteriore passo consiste nel mettere in sicurezza le API e documentarle.
Per qualsiasi chiarimento inserisci un commento qui sotto.
Quasi dimenticavo! Per ringraziarti, ho caricato per te il codice di questo esempio su GitHub a questo indirizzo.