Questa è la parte finale della nostra panoramica di REST. Nelle parti precedenti abbiamo trattato:
fai clic sul pulsante "Avanti". Nella finestra successiva, specifica "Maven Project" come tipo di progetto, specifica "Group" e "Artifact":
fai clic sul pulsante "Next". Nella finestra successiva, dobbiamo selezionare i componenti Spring Framework necessari per il progetto. Spring Web ci basterà:
Fare clic sul pulsante "Avanti". Ora non resta che indicare il nome del progetto e la sua posizione nel file system:
fare clic sul pulsante "Fine". Il progetto è stato creato e ora possiamo vederne la struttura: IDEA ha generato per noi
un descrittore di distribuzione Maven (pom.xml) e la classe principale dell'applicazione ( ).
fai clic sul pulsante "Nuovo" nell'angolo in alto a sinistra. Quindi, seleziona "Richiedi":
Quindi, assegnagli un nome e salvalo. Ora proviamo ad inviare una richiesta POST al server e creiamo il primo cliente:
Creiamo diversi clienti in questo modo. Quindi cambiamo il tipo di richiesta in GET e inviamo la richiesta al server:

Creazione di un progetto
In questa sezione creeremo una piccola applicazione RESTful utilizzando Spring Boot. La nostra applicazione implementerà le operazioni CRUD (Create, Read, Update, Delete) sui clienti dall'esempio nella parte precedente della panoramica. Per iniziare, creeremo una nuova applicazione Spring Boot tramite il menu: File -> Nuovo -> Progetto... Nella finestra che si apre, seleziona Spring Initializr e specifica l'SDK del progetto:




RestExampleApplication
Ecco come appaiono:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.codegym.lessons/groupId>
<artifactId>rest_example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rest_example</name>
<description>REST example project</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
RestEsempioApplicazione:
@SpringBootApplication
public class RestExampleApplication {
public static void main(String[] args) {
SpringApplication.run(RestExampleApplication.class, args);
}
}
Creazione della funzionalità REST
La nostra applicazione è un sistema di gestione dei clienti. Quindi, la prima cosa che dobbiamo fare è creare un'entità cliente. Sarà una classe POJO (plain old Java object). Creare unmodel
pacchetto all'interno del com.codegym.lessons.rest_example
pacchetto. All'interno del model
pacchetto, crea Customer
:
public class Customer {
private Integer id;
private String name;
private String email;
private String phone;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
Il servizio implementerà le operazioni CRUD sui clienti. Il passaggio successivo consiste nel creare un servizio che implementerà queste operazioni. Nel com.codegym.lessons.rest_example
pacchetto, crea un service
pacchetto. E al suo interno, crea CustomerService
un'interfaccia. Ecco il codice dell'interfaccia con i commenti:
public interface CustomerService {
/**
* Creates a new customer
* @param customer - Customer to be created
*/
void create(Customer customer);
/**
* Returns a list of all existing customers
* @return List of customers
*/
List<Customer> readAll();
/**
* Returns a customer based on its ID
* @param id - Customer ID
* @return - Customer object with the given ID
*/
Customer read(int id);
/**
* Updates the customer with the given ID,
* according to the passed customer
* @param customer - Customer to use to update the data
* @param id - ID of the customer you want to update
* @return - true if the data has been updated, otherwise false
*/
boolean update(Customer customer, int id);
/**
* Deletes the customer with the given ID
* @param id - ID of the customer to be deleted
* @return - true if the customer was deleted, otherwise false
*/
boolean delete(int id);
}
Successivamente, dobbiamo implementare questa interfaccia. Ora Map<Integer, Customer>
memorizzeremo i nostri clienti. Le chiavi della mappa saranno gli ID cliente ei valori saranno i clienti stessi. Questo viene fatto in modo da non sovraccaricare questo esempio con le specifiche di lavorare con un database reale. Tuttavia, in futuro potremo scrivere un'altra implementazione dell'interfaccia, che consentirà di connettersi a un vero database. Nel service
pacchetto, crea un'implementazione dell'interfaccia CustomerService
:
@Service
public class CustomerServiceImpl implements CustomerService {
// Customer repository
private static final Map<Integer, Customer> CUSTOMER_REPOSITORY_MAP = new HashMap<>();
// Variable for generating a customer ID
private static final AtomicInteger CUSTOMER_ID_HOLDER = new AtomicInteger();
@Override
public void create(Customer customer) {
final int customerId = CUSTOMER_ID_HOLDER.incrementAndGet();
customer.setId(customerId);
CUSTOMER_REPOSITORY_MAP.put(customerId, customer);
}
@Override
public List<Customer> readAll() {
return new ArrayList<>(CUSTOMER_REPOSITORY_MAP.values());
}
@Override
public Customer read(int id) {
return CUSTOMER_REPOSITORY_MAP.get(id);
}
@Override
public boolean update(Customer customer, int id) {
if (CUSTOMER_REPOSITORY_MAP.containsKey(id)) {
customer.setId(id);
CUSTOMER_REPOSITORY_MAP.put(id, customer);
return true;
}
return false;
}
@Override
public boolean delete(int id) {
return CUSTOMER_REPOSITORY_MAP.remove(id) != null;
}
}
L' @Service
annotazione dice a Spring che questa classe è un servizio. Si tratta di un tipo speciale di classe che implementa alcune logiche di applicazione aziendale. Successivamente, grazie a questa annotazione, Spring utilizzerà l'iniezione di dipendenza per fornirci un'istanza di questa classe in tutti i luoghi in cui è necessaria. Ora è il momento di creare un controller. Questa è una classe speciale in cui implementeremo la logica per l'elaborazione delle richieste dei client inviate agli endpoint (URI). Per rendere tutto questo più chiaro, creeremo questa classe in modo incrementale. Innanzitutto, crea la classe stessa e aggiungi una dipendenza su CustomerService
:
@RestController
public class CustomerController {
private final CustomerService customerService;
@Autowired
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
}
Spieghiamo le annotazioni: @RestController dice a Spring che questa classe è un controller REST. In altre parole, questa classe implementa la logica per l'elaborazione delle richieste del client. @Autowired dice a Spring che è necessario aggiungere una dipendenza qui. Passiamo l' CustomerService
interfaccia al costruttore. In precedenza, abbiamo contrassegnato l'implementazione di questo servizio con l' @Service
annotazione e ora Spring sarà in grado di passare un'istanza di questa implementazione al costruttore del controller. Successivamente, implementeremo ciascun metodo del controller per la gestione delle operazioni CRUD. Iniziamo con l'operazione di creazione. Per fare ciò, scriviamo un create
metodo:
@PostMapping(value = "/customers")
public ResponseEntity<?> create(@RequestBody Customer customer) {
customerService.create(customer);
return new ResponseEntity<>(HttpStatus.CREATED);
}
Analizziamo questo metodo: @PostMapping(value = "/customers")
significa che questo metodo elabora le richieste POST inviate all'indirizzo "/clienti". Il metodo restituisce un ResponseEntity<?>
. A ResponseEntity
è una classe speciale per la restituzione delle risposte. Successivamente, lo useremo per restituire un codice di stato HTTP al client. Il metodo ha un @RequestBody Customer customer
parametro. Il valore di questo parametro deriva dal corpo della richiesta. L' @RequestBody
annotazione lo indica. All'interno del corpo del metodo, chiamiamo il create()
metodo sul servizio precedentemente creato e lo passiamo al controller del cliente ricevuto nei parametri. Quindi restituiamo lo stato "201 Created" creando un nuovo ResponseEntity
oggetto e passandogli il HttpStatus
campo enum corrispondente. Successivamente, implementeremo ilread
operazione: Innanzitutto, implementeremo l'operazione per ottenere un elenco di tutti i clienti disponibili:
@GetMapping(value = "/customers")
public ResponseEntity<List<Customer>> read() {
final List<Customer> customers = customerService.readAll();
return customers != null && !customers.isEmpty()
? new ResponseEntity<>(customers, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Immergiamoci in: @GetMapping(value = "/customers")
- tutto qui è simile all'annotazione @PostMapping
, ma ora stiamo elaborando le richieste GET. Questa volta restituiamo un ResponseEntity<List<Customer>>
, e oltre a uno stato HTTP, restituiremo anche un corpo della risposta, che sarà l'elenco dei clienti. Nei controller REST di Spring, tutto è costituito da oggetti POJO e raccolte di oggetti POJO, che vengono restituiti come corpi di risposta e serializzati automaticamente in JSON, se non diversamente specificato. Questo ci si addice perfettamente. All'interno del metodo, utilizziamo il nostro servizio per ottenere un elenco di tutti i clienti. Successivamente, se l'elenco non è nullo e non è vuoto, allora usiamo il fileResponseEntity
class per restituire l'elenco dei clienti e il codice di stato HTTP "200 OK". Altrimenti, restituiamo semplicemente il codice di stato HTTP "404 Not Found". Ora implementeremo la possibilità di ottenere un cliente utilizzando il suo ID:
@GetMapping(value = "/customers/{id}")
public ResponseEntity<Customer> read(@PathVariable(name = "id") int id) {
final Customer customer = customerService.read(id);
return customer != null
? new ResponseEntity<>(customer, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Una novità qui è la variabile path. La variabile è definita nell'URI: value = "/customers/{id}"
. Lo indichiamo tra parentesi graffe. E lo riceviamo come int
parametro di metodo usando l' @PathVariable(name = "id")
annotazione. Questo metodo accetterà le richieste inviate agli URI nella forma /customers/{id}
, dove {id}
rappresenta qualsiasi valore numerico. Questo valore viene successivamente passato tramite la int id
variabile al parametro del metodo. Nel corpo, otteniamo l' Customer
oggetto utilizzando il nostro servizio e il ricevuto id
. E poi, per analogia con l'elenco, restituiamo lo stato "200 OK" e l' Customer
oggetto stesso, o semplicemente lo stato "404 Not Found" se il sistema non ha clienti con quello id
. Dobbiamo ancora implementare due operazioni: aggiornamento ed eliminazione. Ecco il codice per questi metodi:
@PutMapping(value = "/customers/{id}")
public ResponseEntity<?> update(@PathVariable(name = "id") int id, @RequestBody Customer customer) {
final boolean updated = customerService.update(customer, id);
return updated
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
@DeleteMapping(value = "/customers/{id}")
public ResponseEntity<?> delete(@PathVariable(name = "id") int id) {
final boolean deleted = customerService.delete(id);
return deleted
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
Non c'è nulla di essenzialmente nuovo in questi metodi, quindi salteremo la descrizione dettagliata. L'unica cosa degna di nota è che il update()
metodo gestisce le richieste PUT ( @PutMapping
annotazione) e il delete()
metodo gestisce le richieste DELETE ( DeleteMapping
annotazione). Ecco il codice completo per il controller:
@RestController
public class CustomerController {
private final CustomerService customerService;
@Autowired
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
@PostMapping(value = "/customers")
public ResponseEntity<?> create(@RequestBody Customer customer) {
customerService.create(customer);
return new ResponseEntity<>(HttpStatus.CREATED);
}
@GetMapping(value = "/customers")
public ResponseEntity<List<Customer>> read() {
final List<Customer> customers = customerService.readAll();
return customers != null && !customers.isEmpty()
? new ResponseEntity<>(customers, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
@GetMapping(value = "/customers/{id}")
public ResponseEntity<Customer> read(@PathVariable(name = "id") int id) {
final Customer customer = customerService.read(id);
return customer != null
? new ResponseEntity<>(customer, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
@PutMapping(value = "/customers/{id}")
public ResponseEntity<?> update(@PathVariable(name = "id") int id, @RequestBody Customer customer) {
final boolean updated = customerService.update(customer, id);
return updated
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
@DeleteMapping(value = "/customers/{id}")
public ResponseEntity<?> delete(@PathVariable(name = "id") int id) {
final boolean deleted = customerService.delete(id);
return deleted
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
}
Di conseguenza, la struttura del nostro progetto è la seguente: 
Lancio e test
Per avviare la nostra applicazione, basta eseguire ilmain()
metodo nella RestExampleApplication
classe. Ma per testare i servizi Web RESTful, dobbiamo scaricare software aggiuntivo. Il fatto è che le richieste GET sono abbastanza semplici da inviare da un normale browser, ma un normale browser non può inviare richieste POST, PUT e DELETE. Non preoccuparti: puoi utilizzare un programma chiamato Postman per inviare qualsiasi richiesta HTTP. Puoi scaricarlo qui . Dopo aver scaricato e installato Postman, iniziamo a testare la nostra applicazione. Per fare ciò, apri il programma e crea una nuova richiesta: 



Riepilogo
Congratulazioni! Abbiamo coperto sufficientemente REST. C'era un grande volume di materiale, ma spero che ti sia stato utile:-
Abbiamo imparato cos'è REST.
-
Abbiamo appreso come è nato REST.
-
Abbiamo parlato dei limiti e dei principi alla base di questo stile architettonico:
- architettura client-server
- apolide
- memorizzazione nella cache
- interfaccia uniforme
- strati
- codice su richiesta (facoltativo)
-
Abbiamo esplorato i vantaggi offerti da REST
-
Abbiamo esaminato in dettaglio come il server e il client interagiscono tra loro tramite il protocollo HTTP.
-
Abbiamo esaminato più da vicino le richieste e le risposte. Abbiamo sezionato le loro parti costitutive.
-
Infine, abbiamo acquisito esperienza pratica scrivendo la nostra piccola applicazione RESTful utilizzando Spring Boot. E abbiamo anche imparato a testarlo usando Postman.
Compiti a casa
Prova quanto segue:- Seguendo la descrizione precedente, crea il tuo progetto Spring Boot e implementa la stessa logica della lezione. Ripeti tutto esattamente.
- Avvia l'applicazione.
- Scarica e configura Postman (o qualsiasi altro strumento per l'invio di richieste, ad esempio curl).
- Testare le richieste POST e GET nello stesso modo descritto nella lezione.
- Testa tu stesso le richieste PUT e DELETE.
GO TO FULL VERSION