Aceasta este partea finală a prezentării noastre de ansamblu asupra REST. În părțile anterioare, am acoperit: Prezentare generală a REST.  Partea 3: Construirea unui serviciu RESTful pe Spring Boot - 1

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: Prezentare generală a REST.  Partea 3: Construirea unui serviciu RESTful pe Spring Boot - 2Faceți clic pe butonul „Next”. În fereastra următoare, specificați „Proiect Maven” ca tip de proiect, specificați „Grupul” și „Artefactul”: Prezentare generală a REST.  Partea 3: Construirea unui serviciu RESTful pe Spring Boot - 3Faceț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: Prezentare generală a REST.  Partea 3: Construirea unui serviciu RESTful pe Spring Boot - 4Faceț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: Prezentare generală a REST.  Partea 3: Construirea unui serviciu RESTful pe Spring Boot - 5Faceți clic pe butonul „Finish”. Proiectul este creat și acum îi putem vedea structura: Prezentare generală a REST.  Partea 3: Construirea unui serviciu RESTful pe Spring Boot - 6IDEA 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 un modelpachet în interiorul com.codegym.lessons.rest_examplepachetului. În interiorul modelpachetului, 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_examplepachet, creați un servicepachet. Și în interiorul acestuia, creați o CustomerServiceinterfață. 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 servicepachet, creați o implementare a CustomerServiceinterfeț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 CustomerServiceinterfața constructorului. Anterior, am marcat implementarea acestui serviciu cu @Serviceadnotarea, 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 createmetodă:

@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 ResponseEntityeste 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 customerparametru. Valoarea acestui parametru provine din corpul cererii. Adnotarea @RequestBodyindică 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 ResponseEntityobiect și transmiterea HttpStatuscâmpului de enumerare corespunzător acestuia. În continuare, vom implementareadoperaț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 @PostMappingadnotarea, 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 folosimResponseEntityclasa 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 intparametru 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 idvariabilă la parametrul metodei. În corp, obținem obiectul Customerfolosind 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 ( @PutMappingadnotare), iar delete()metoda se ocupă de cererile DELETE ( DeleteMappingadnotare). 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: Prezentare generală a REST.  Partea 3: Construirea unui serviciu RESTful pe Spring Boot - 7

Lansare și testare

Pentru a porni aplicația noastră, trebuie doar să rulați main()metoda în RestExampleApplicationclasă. 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: Prezentare generală a REST.  Partea 3: Construirea unui serviciu RESTful pe Spring Boot - 9Faceți clic pe butonul „Nou” din colțul din stânga sus. Apoi, selectați „Solicitare”: Prezentare generală a REST.  Partea 3: Construirea unui serviciu RESTful pe Spring Boot - 10Apoi, 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: Prezentare generală a REST.  Partea 3: Construirea unui serviciu RESTful pe Spring Boot - 11Creăm mai mulți clienți în acest fel. Apoi schimbăm tipul cererii în GET și trimitem cererea către server: Prezentare generală a REST.  Partea 3: Construirea unui serviciu RESTful pe Spring Boot - 12

rezumat

Felicitări! Am acoperit suficient REST. A existat un volum mare de material, dar sper că v-a fost util:
  1. Am învățat ce este REST.

  2. Am aflat despre cum a apărut REST.

  3. 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)
  4. Am explorat beneficiile oferite de REST

  5. Am examinat în detaliu modul în care serverul și clientul interacționează unul cu celălalt prin protocolul HTTP.

  6. Am analizat mai atent solicitările și răspunsurile. Le-am disecat părțile constitutive.

  7. Î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.

Uf. Au fost multe, dar mai ai ceva de făcut ca teme.

Teme pentru acasă

Încercați următoarele:
  1. 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.
  2. Lansați aplicația.
  3. Descărcați și configurați Postman (sau orice alt instrument pentru trimiterea cererilor, de exemplu, curl).
  4. Testați cererile POST și GET în același mod descris în lecție.
  5. Testați singur solicitările PUT și DELETE.