Aceasta este partea finală a prezentării noastre de ansamblu asupra REST. În părțile anterioare, am acoperit:
Crearea unui proiect
În această secțiune, vom crea o mică aplicație RESTful folosind Spring Boot. Aplicația noastră va implementa operațiuni CRUD (Creare, Read, Update, Delete) asupra clienților din exemplul din partea anterioară a prezentării generale. Pentru a începe, vom crea o nouă aplicație Spring Boot prin meniul: File -> New -> Project... În fereastra care se deschide, selectați Spring Initializr și specificați Project SDK: Faceți clic pe butonul „Next”. În fereastra următoare, specificați „Proiect Maven” ca tip de proiect, specificați „Grupul” și „Artefactul”: Faceți clic pe butonul „Următorul”. În fereastra următoare, trebuie să selectăm componentele Spring Framework necesare proiectului. Spring Web ne va fi suficient: Faceți clic pe butonul „Următorul”. Acum nu mai rămâne decât să indicați numele proiectului și locația acestuia în sistemul de fișiere: Faceți clic pe butonul „Finish”. Proiectul este creat și acum îi putem vedea structura: IDEA a generat un descriptor de implementare Maven (pom.xml) și clasa principală a aplicației (RestExampleApplication
) pentru noi. Iată cum arată ele:
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>
RestExampleApplication:
@SpringBootApplication
public class RestExampleApplication {
public static void main(String[] args) {
SpringApplication.run(RestExampleApplication.class, args);
}
}
Crearea funcționalității REST
Aplicația noastră este un sistem de management al clienților. Deci, primul lucru pe care trebuie să-l facem este să creăm o entitate client. Va fi o clasă POJO (obiect Java simplu vechi). Creați unmodel
pachet în interiorul com.codegym.lessons.rest_example
pachetului. În interiorul model
pachetului, creați 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;
}
}
Serviciul va implementa operațiuni CRUD asupra clienților. Următorul pas este crearea unui serviciu care va implementa aceste operațiuni. În com.codegym.lessons.rest_example
pachet, creați un service
pachet. Și în interiorul acestuia, creați o CustomerService
interfață. Iată codul interfeței cu comentarii:
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);
}
În continuare, trebuie să implementăm această interfață. Acum Map<Integer, Customer>
va stoca clienții noștri. Cheile hărții vor fi ID-urile clienților, iar valorile vor fi clienții înșiși. Acest lucru se face pentru a nu supraîncărca acest exemplu cu specificul lucrului cu o bază de date reală. Cu toate acestea, în viitor vom putea scrie o altă implementare a interfeței, care va face posibilă conectarea la o bază de date reală. În service
pachet, creați o implementare a CustomerService
interfeței:
@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;
}
}
Adnotarea @Service
îi spune primăverii că această clasă este un serviciu. Acesta este un tip special de clasă care implementează o anumită logică a aplicațiilor de afaceri. Ulterior, datorită acestei adnotări, Spring va folosi injecția de dependență pentru a ne oferi o instanță a acestei clase în toate locurile unde este nevoie. Acum este timpul să creați un controler. Aceasta este o clasă specială în care vom implementa logica pentru procesarea cererilor client trimise către punctele finale (URI). Pentru a clarifica toate acestea, vom crea această clasă treptat. Mai întâi, creați clasa în sine și adăugați o dependență de CustomerService
:
@RestController
public class CustomerController {
private final CustomerService customerService;
@Autowired
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
}
Să explicăm adnotările: @RestController îi spune lui Spring că această clasă este un controler REST. Cu alte cuvinte, această clasă implementează logica pentru procesarea cererilor clientului. @Autowired îi spune lui Spring că aici trebuie adăugată o dependență. Transmitem CustomerService
interfața constructorului. Anterior, am marcat implementarea acestui serviciu cu @Service
adnotarea, iar acum Spring va putea transmite o instanță a acestei implementări constructorului controlerului. În continuare, vom implementa fiecare metodă de controler pentru gestionarea operațiunilor CRUD. Să începem cu operația de creare. Pentru a face acest lucru, scriem o create
metodă:
@PostMapping(value = "/customers")
public ResponseEntity<?> create(@RequestBody Customer customer) {
customerService.create(customer);
return new ResponseEntity<>(HttpStatus.CREATED);
}
Să analizăm această metodă: @PostMapping(value = "/customers")
înseamnă că această metodă procesează cererile POST trimise la adresa „/clienți”. Metoda returnează un ResponseEntity<?>
. A ResponseEntity
este o clasă specială pentru returnarea răspunsurilor. Mai târziu, îl vom folosi pentru a returna clientului un cod de stare HTTP. Metoda are un @RequestBody Customer customer
parametru. Valoarea acestui parametru provine din corpul cererii. Adnotarea @RequestBody
indică acest lucru. În interiorul corpului metodei, apelăm create()
metoda pe serviciul creat anterior și o transmitem controlerului clientului primit în parametri. Apoi returnăm starea „201 Created” prin crearea unui nou ResponseEntity
obiect și transmiterea HttpStatus
câmpului de enumerare corespunzător acestuia. În continuare, vom implementaread
operațiune: În primul rând, vom implementa operația pentru a obține o listă cu toți clienții 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);
}
Să ne aprofundăm: @GetMapping(value = "/customers")
— totul aici este similar cu @PostMapping
adnotarea, dar acum procesăm cereri GET. De data aceasta returnăm un ResponseEntity<List<Customer>>
, și pe lângă o stare HTTP, vom returna și un corp de răspuns, care va fi lista clienților. În controlerele REST de la Spring, totul sunt obiecte POJO și colecții de obiecte POJO, care sunt returnate ca corpuri de răspuns și serializate automat în JSON, dacă nu se specifică altfel. Acest lucru ni se potrivește perfect. În cadrul metodei, folosim serviciul nostru pentru a obține o listă a tuturor clienților. Apoi, dacă lista nu este nulă și nu este goală, atunci folosimResponseEntity
clasa pentru a returna lista de clienți și codul de stare HTTP „200 OK”. În caz contrar, pur și simplu returnăm codul de stare HTTP „404 Not Found”. Acum vom implementa capacitatea de a obține un client folosind ID-ul său:
@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);
}
Un lucru nou aici este variabila cale. Variabila este definită în URI: value = "/customers/{id}"
. Îl indicăm în acolade. Și îl primim ca int
parametru de metodă folosind @PathVariable(name = "id")
adnotarea. Această metodă va accepta cererile trimise către URI-uri în forma /customers/{id}
, unde {id}
reprezintă orice valoare numerică. Această valoare este transmisă ulterior prin int id
variabilă la parametrul metodei. În corp, obținem obiectul Customer
folosind serviciul nostru și primit id
. Și apoi, prin analogie cu lista, returnăm fie starea „200 OK” și obiectul Customer
în sine, fie pur și simplu starea „404 Not Found” dacă sistemul nu are niciun client cu asta id
. Mai trebuie să implementăm două operațiuni: actualizare și ștergere. Iată codul pentru aceste metode:
@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);
}
Nu există nimic în esență nou în aceste metode, așa că vom sări peste descrierea detaliată. Singurul lucru care merită menționat este că update()
metoda se ocupă de cererile PUT ( @PutMapping
adnotare), iar delete()
metoda se ocupă de cererile DELETE ( DeleteMapping
adnotare). Iată codul complet pentru controler:
@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);
}
}
Ca urmare, structura proiectului nostru este următoarea:
Lansare și testare
Pentru a porni aplicația noastră, trebuie doar să rulațimain()
metoda în RestExampleApplication
clasă. Dar pentru a testa serviciile web RESTful, trebuie să descarcăm software suplimentar. Faptul este că cererile GET sunt destul de simplu de trimis dintr-un browser obișnuit, dar un browser obișnuit nu poate trimite cereri POST, PUT și DELETE. Nu vă faceți griji: puteți utiliza un program numit Postman pentru a trimite orice solicitări HTTP. Îl poți descărca aici . După descărcarea și instalarea Postman, începem să testăm aplicația noastră. Pentru a face acest lucru, deschideți programul și creați o nouă solicitare: Faceți clic pe butonul „Nou” din colțul din stânga sus. Apoi, selectați „Solicitare”: Apoi, dați-i un nume și salvați-l. Acum să încercăm să trimitem o solicitare POST către server și să creăm primul client: Creăm mai mulți clienți în acest fel. Apoi schimbăm tipul cererii în GET și trimitem cererea către server:
rezumat
Felicitări! Am acoperit suficient REST. A existat un volum mare de material, dar sper că v-a fost util:-
Am învățat ce este REST.
-
Am aflat despre cum a apărut REST.
-
Am vorbit despre limitările și principiile din spatele acestui stil arhitectural:
- arhitectura client-server
- Fara stare
- stocarea în cache
- interfață uniformă
- straturi
- cod la cerere (optional)
-
Am explorat beneficiile oferite de REST
-
Am examinat în detaliu modul în care serverul și clientul interacționează unul cu celălalt prin protocolul HTTP.
-
Am analizat mai atent solicitările și răspunsurile. Le-am disecat părțile constitutive.
-
În cele din urmă, am dobândit ceva experiență practică prin scrierea propriei noastre mici aplicații RESTful folosind Spring Boot. Și chiar am învățat cum să-l testăm folosind Postman.
Teme pentru acasă
Încercați următoarele:- Urmând descrierea de mai sus, creați-vă propriul proiect Spring Boot și implementați aceeași logică ca în lecție. Repetați totul exact.
- Lansați aplicația.
- Descărcați și configurați Postman (sau orice alt instrument pentru trimiterea cererilor, de exemplu, curl).
- Testați cererile POST și GET în același mod descris în lecție.
- Testați singur solicitările PUT și DELETE.
GO TO FULL VERSION