CodeGym /Blog Java /Random-PL /Omówienie REST. Część 3: Budowa usługi RESTful na Spring ...
John Squirrels
Poziom 41
San Francisco

Omówienie REST. Część 3: Budowa usługi RESTful na Spring Boot

Opublikowano w grupie Random-PL
To jest ostatnia część naszego przeglądu REST. W poprzednich częściach omówiliśmy: Omówienie REST.  Część 3: Budowa usługi RESTful na Spring Boot — 1

Tworzenie projektu

W tej sekcji stworzymy małą aplikację RESTful przy użyciu Spring Boot. Nasza aplikacja zaimplementuje operacje CRUD (Create, Read, Update, Delete) na klientach z przykładu z poprzedniej części przeglądu. Na początek utworzymy nową aplikację Spring Boot poprzez menu: Plik -> Nowy -> Projekt... W oknie, które zostanie otwarte, wybierz Spring Initializr i określ SDK projektu: Omówienie REST.  Część 3: Budowa usługi RESTful na Spring Boot - 2Kliknij przycisk „Dalej”. W następnym oknie określ „Projekt Maven” jako typ projektu, określ „Grupę” i „Artefakt”: Omówienie REST.  Część 3: Budowa usługi RESTful na Spring Boot — 3Kliknij przycisk „Dalej”. W kolejnym oknie musimy wybrać potrzebne do projektu komponenty Spring Framework. Wystarczy nam Spring Web: Omówienie REST.  Część 3: Budowa usługi RESTful na Spring Boot — 4Kliknij przycisk „Dalej”. Teraz pozostaje tylko wskazać nazwę projektu i jego lokalizację w systemie plików: Omówienie REST.  Część 3: Budowa usługi RESTful na Spring Boot — 5Kliknij przycisk „Zakończ”. Projekt jest tworzony i teraz możemy zobaczyć jego strukturę: IDEA wygenerowała dla nas Omówienie REST.  Część 3: Budowa usługi RESTful na Spring Boot — 6deskryptor wdrożenia Mavena (pom.xml) oraz główną klasę aplikacji ( ). RestExampleApplicationOto jak wyglądają:

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>
RestExampleAplikacja:

@SpringBootApplication
public class RestExampleApplication {

   public static void main(String[] args) {
       SpringApplication.run(RestExampleApplication.class, args);
   }

}

Tworzenie funkcjonalności REST

Nasza aplikacja to system zarządzania klientami. Pierwszą rzeczą, którą musimy zrobić, to utworzyć podmiot klienta. Będzie to klasa POJO (zwykły stary obiekt Java). Utwórz modelpakiet wewnątrz com.codegym.lessons.rest_examplepakietu. Wewnątrz modelpakietu utwórz 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;
   }
}
Usługa zaimplementuje operacje CRUD na klientach. Kolejnym krokiem jest stworzenie usługi, która będzie realizowała te operacje. W com.codegym.lessons.rest_examplepakiecie utwórz servicepakiet. A wewnątrz tego utwórz CustomerServiceinterfejs. Oto kod interfejsu z komentarzami:

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);
}
Następnie musimy zaimplementować ten interfejs. Teraz Map<Integer, Customer>będzie przechowywać naszych klientów. Kluczami mapy będą identyfikatory klientów, a wartościami będą sami klienci. Odbywa się to tak, aby nie przeciążać tego przykładu specyfiką pracy z prawdziwą bazą danych. Jednak w przyszłości będziemy mogli napisać inną implementację interfejsu, która umożliwi połączenie z prawdziwą bazą danych. W servicepaczce utwórz implementację interfejsu 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;
   }
}
Adnotacja @Servicemówi Springowi, że ta klasa jest usługą. Jest to specjalny typ klasy, który implementuje pewną logikę aplikacji biznesowej. Następnie, dzięki tej adnotacji, Spring użyje wstrzykiwania zależności, aby dostarczyć nam instancję tej klasy we wszystkich miejscach, w których jest to potrzebne. Teraz czas na utworzenie kontrolera. Jest to specjalna klasa, w której zaimplementujemy logikę przetwarzania żądań klientów wysyłanych do punktów końcowych (URI). Aby to wszystko było bardziej przejrzyste, będziemy tworzyć tę klasę stopniowo. Najpierw utwórz samą klasę i dodaj zależność od CustomerService:

@RestController
public class CustomerController {

   private final CustomerService customerService;

   @Autowired
   public CustomerController(CustomerService customerService) {
       this.customerService = customerService;
   }
}
Wyjaśnijmy adnotacje: @RestController mówi Springowi, że ta klasa jest kontrolerem REST. Innymi słowy, ta klasa implementuje logikę przetwarzania żądań klientów. @Autowired mówi Springowi, że należy tutaj dodać zależność. Przekazujemy CustomerServiceinterfejs do konstruktora. Wcześniej adnotacją oznaczyliśmy implementację tej usługi @Service, a teraz Spring będzie mógł przekazać instancję tej implementacji do konstruktora kontrolera. Następnie zaimplementujemy każdą metodę kontrolera do obsługi operacji CRUD. Zacznijmy od operacji tworzenia. W tym celu piszemy createmetodę:

@PostMapping(value = "/customers")
public ResponseEntity<?> create(@RequestBody Customer customer) {
   customerService.create(customer);
   return new ResponseEntity<>(HttpStatus.CREATED);
}
Przeanalizujmy tę metodę: @PostMapping(value = "/customers")oznacza to, że ta metoda przetwarza żądania POST wysyłane na adres „/customers”. Metoda zwraca ResponseEntity<?>. A ResponseEntityjest specjalną klasą do zwracania odpowiedzi. Później użyjemy go do zwrócenia klientowi kodu stanu HTTP. Metoda ma @RequestBody Customer customerparametr. Wartość tego parametru pochodzi z treści żądania. Wskazuje na to adnotacja @RequestBody. Wewnątrz ciała metody wywołujemy create()metodę na utworzonej wcześniej usłudze i przekazujemy ją otrzymanemu w parametrach kontrolerowi klienta. Następnie zwracamy stan „201 Created” tworząc nowy obiekt i przekazując mu ResponseEntityodpowiednie pole enum. HttpStatusNastępnie zaimplementujemyreadoperacja: Najpierw zaimplementujemy operację, aby uzyskać listę wszystkich dostępnych klientów:

@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);
}
Przejdźmy do rzeczy: @GetMapping(value = "/customers")— wszystko tutaj jest podobne do @PostMappingadnotacji, ale teraz przetwarzamy żądania GET. Tym razem zwrócimy ResponseEntity<List<Customer>>, a oprócz statusu HTTP zwrócimy również treść odpowiedzi, którą będzie lista klientów. W kontrolerach REST Springa wszystko jest obiektami POJO i kolekcjami obiektów POJO, które są zwracane jako treść odpowiedzi i automatycznie serializowane do formatu JSON, chyba że określono inaczej. To nam pasuje idealnie. W ramach tej metody korzystamy z naszej usługi, aby uzyskać listę wszystkich klientów. Następnie, jeśli lista nie jest pusta i nie jest pusta, używamy metodyResponseEntityclass, aby zwrócić listę klientów i kod stanu HTTP „200 OK”. W przeciwnym razie po prostu zwracamy kod stanu HTTP „404 Not Found”. Teraz zaimplementujemy możliwość pozyskiwania klienta za pomocą jego 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);
}
Nowością jest tutaj zmienna path. Zmienna jest zdefiniowana w URI: value = "/customers/{id}". Oznaczamy to w nawiasach klamrowych. I otrzymujemy go jako intparametr metody za pomocą @PathVariable(name = "id")adnotacji. Ta metoda akceptuje żądania wysyłane do identyfikatorów URI w postaci /customers/{id}, gdzie {id}reprezentuje dowolną wartość liczbową. Ta wartość jest następnie przekazywana przez int idzmienną do parametru metody. W zabudowie otrzymujemy przedmiot Customerkorzystając z naszej usługi oraz otrzymany id. I wtedy, analogicznie do listy, zwracamy albo stan „200 OK” i Customersam obiekt, albo po prostu stan „404 Not Found”, jeśli w systemie nie ma żadnego klienta z tym id. Musimy jeszcze zaimplementować dwie operacje: update i delete. Oto kod tych metod:

@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);
}
W tych metodach nie ma nic zasadniczo nowego, więc pominiemy szczegółowy opis. Jedyne, o czym warto wspomnieć, to że update()metoda obsługuje żądania PUT ( @PutMappingadnotacja), a delete()metoda obsługuje żądania DELETE ( DeleteMappingadnotacja). Oto pełny kod kontrolera:

@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);
   }
}
W rezultacie struktura naszego projektu wygląda następująco: Omówienie REST.  Część 3: Budowa usługi RESTful na Spring Boot — 7

Uruchomienie i testowanie

Aby uruchomić naszą aplikację wystarczy uruchomić main()metodę w RestExampleApplicationklasie. Ale aby przetestować usługi sieciowe RESTful, musimy pobrać dodatkowe oprogramowanie. Faktem jest, że żądania GET są dość proste do wysłania ze zwykłej przeglądarki, ale zwykła przeglądarka nie może wysyłać żądań POST, PUT i DELETE. Nie martw się: do wysyłania dowolnych żądań HTTP możesz użyć programu o nazwie Postman. Możesz go pobrać tutaj . Po pobraniu i zainstalowaniu Postmana przystępujemy do testowania naszej aplikacji. Aby to zrobić, otwórz program i utwórz nowe zgłoszenie: Omówienie REST.  Część 3: Budowa usługi RESTful na Spring Boot - 9Kliknij przycisk „Nowe” w lewym górnym rogu. Następnie wybierz „Żądanie”: Omówienie REST.  Część 3: Budowa usługi RESTful na Spring Boot — 10Następnie nadaj mu nazwę i zapisz. Spróbujmy teraz wysłać żądanie POST do serwera i utworzyć pierwszego klienta: Omówienie REST.  Część 3: Budowa usługi RESTful na Spring Boot - 11Tworzymy w ten sposób kilku klientów. Następnie zmieniamy typ żądania na GET i wysyłamy żądanie do serwera: Omówienie REST.  Część 3: Budowa usługi RESTful na Spring Boot - 12

Streszczenie

Gratulacje! Wystarczająco omówiliśmy REST. Było dużo materiału, ale mam nadzieję, że był dla Ciebie przydatny:
  1. Dowiedzieliśmy się, czym jest REST.

  2. Dowiedzieliśmy się, jak powstał REST.

  3. Rozmawialiśmy o ograniczeniach i zasadach stojących za tym stylem architektonicznym:

    • architektura klient-serwer
    • bezpaństwowiec
    • buforowanie
    • jednolity interfejs
    • warstwy
    • kod na żądanie (opcjonalnie)
  4. Zbadaliśmy korzyści zapewniane przez REST

  5. Zbadaliśmy szczegółowo, w jaki sposób serwer i klient współdziałają ze sobą za pośrednictwem protokołu HTTP.

  6. Przyjrzeliśmy się bliżej prośbom i odpowiedziom. Przeanalizowaliśmy ich części składowe.

  7. W końcu zdobyliśmy praktyczne doświadczenie, pisząc naszą własną małą aplikację RESTful przy użyciu Spring Boot. Nauczyliśmy się nawet, jak to przetestować za pomocą Postmana.

Uff. To dużo, ale wciąż jest coś do zrobienia w ramach pracy domowej.

Praca domowa

Spróbuj wykonać następujące czynności:
  1. Postępując zgodnie z powyższym opisem, utwórz własny projekt Spring Boot i zaimplementuj tę samą logikę, co w lekcji. Powtórz wszystko dokładnie.
  2. Uruchom aplikację.
  3. Pobierz i skonfiguruj Postmana (lub dowolne inne narzędzie do wysyłania żądań, na przykład curl).
  4. Przetestuj żądania POST i GET w ten sam sposób, jak opisano w lekcji.
  5. Przetestuj samodzielnie żądania PUT i DELETE.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION