To jest ostatnia część analizy REST. W poprzednich częściach:
Tworzenie projektu
W tej sekcji utworzymy małą aplikację RESTful przy użyciu Spring Boot. Nasza aplikacja zaimplementuje na klientach operacje CRUD (Create, Read, Update, Delete) z przykładu z ostatniej części analizy. Najpierw utwórzmy nową aplikację Spring Boot za pomocą menu Plik -> Nowy -> Projekt... W oknie, które zostanie otwarte, wybierz Spring Individualizr i określ Project SDK: Kliknij przycisk Dalej. W następnym oknie określ typ projektu Maven, określ Grupę i Artefakt: Kliknij przycisk Dalej. W kolejnym oknie musimy wybrać komponenty Spring Framework potrzebne do realizacji projektu. Wystarczy nam Spring Web: Kliknij przycisk Dalej. Następnie pozostaje już tylko określić nazwę projektu i jego lokalizację w systemie plików: Kliknij przycisk Zakończ. Projekt został stworzony, teraz możemy zobaczyć jego strukturę: IDEA wygenerowała dla nas deskryptor wdrażania systemu Maven build - pom.xml oraz główną klasę aplikacji:RestExampleApplication
. Oto ich kod:
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.javarush.lectures</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>
ResztaPrzykładowa aplikacja:
@SpringBootApplication
public class RestExampleApplication {
public static void main(String[] args) {
SpringApplication.run(RestExampleApplication.class, args);
}
}
Tworzenie funkcjonalności REST
Nasza aplikacja zarządza klientami. Pierwszą rzeczą, którą musimy zrobić, to utworzyć encję klienta. Będą to zajęcia POJO. Stwórzmy pakietmodel
wewnątrz pakietu com.javarush.lectures.rest_example
. model
Stwórzmy klasę wewnątrz pakietu Client
:
public class Client {
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 kliencie. Kolejnym krokiem jest stworzenie usługi, która zaimplementuje te operacje. W pakiecie com.javarush.lectures.rest_example
utworzymy pakiet service
, wewnątrz którego utworzymy interfejs ClientService
. Oto kod interfejsu z komentarzami:
public interface ClientService {
/**
* Создает нового клиента
* @param client - клиент для создания
*/
void create(Client client);
/**
* zwroty список всех имеющихся клиентов
* @return список клиентов
*/
List<client> readAll();
/**
* zwroty клиента по его ID
* @param id - ID клиента
* @return - obiekt клиента с заданным ID
*/
Client read(int id);
/**
* Обновляет клиента с заданным ID,
* в соответствии с переданным клиентом
* @param client - клиент в соответсвии с которым нужно обновить данные
* @param id - id клиента которого нужно обновить
* @return - true если данные были обновлены, иначе false
*/
boolean update(Client client, int id);
/**
* Удаляет клиента с заданным ID
* @param id - id клиента, которого нужно удалить
* @return - true если клиент был удален, иначе false
*/
boolean delete(int id);
}
Następnie musimy stworzyć implementację tego interfejsu. Teraz będzie działać jako repozytorium klientów Map<Integer, Client>
. Kluczem karty będzie identyfikator klienta, a wartością będzie sam klient. Dokonano tego, aby nie przeciążać przykładu specyfiką pracy z bazą danych. Jednak w przyszłości będziemy mogli napisać kolejną implementację interfejsu, w którym możliwe będzie podłączenie prawdziwej bazy danych. W pakiecie service
stworzymy implementację interfejsu ClientService
:
@Service
public class ClientServiceImpl implements ClientService {
// ХранLubще клиентов
private static final Map<Integer, Client> CLIENT_REPOSITORY_MAP = new HashMap<>();
// Переменная для генерации ID клиента
private static final AtomicInteger CLIENT_ID_HOLDER = new AtomicInteger();
@Override
public void create(Client client) {
final int clientId = CLIENT_ID_HOLDER.incrementAndGet();
client.setId(clientId);
CLIENT_REPOSITORY_MAP.put(clientId, client);
}
@Override
public List<Client> readAll() {
return new ArrayList<>(CLIENT_REPOSITORY_MAP.values());
}
@Override
public Client read(int id) {
return CLIENT_REPOSITORY_MAP.get(id);
}
@Override
public boolean update(Client client, int id) {
if (CLIENT_REPOSITORY_MAP.containsKey(id)) {
client.setId(id);
CLIENT_REPOSITORY_MAP.put(id, client);
return true;
}
return false;
}
@Override
public boolean delete(int id) {
return CLIENT_REPOSITORY_MAP.remove(id) != null;
}
}
Adnotacja @Service
mówi Springowi, że ta klasa jest usługą. Jest to specjalny typ klasy, w którym zaimplementowana jest pewna logika biznesowa aplikacji. Następnie, dzięki tej adnotacji, Spring udostępni nam instancję tej klasy tam, gdzie jest to potrzebne, za pomocą Zastrzyku zależności. Teraz czas na utworzenie kontrolera. Specjalna klasa, w której implementujemy logikę przetwarzania żądań klientów dla punktów końcowych (URI). Aby było jaśniej, utworzymy tę klasę w częściach. Najpierw utwórzmy samą klasę i wprowadźmy od niej zależność ClientService
:
@RestController
public class ClientController {
private final ClientService clientService;
@Autowired
public ClientController(ClientService clientService) {
this.clientService = clientService;
}
}
Wyjaśnijmy adnotacje: @RestController – informuje Springa, że ta klasa jest kontrolerem REST. Te. ta klasa zaimplementuje logikę przetwarzania żądań klientów @Autowired - mówi Springowi, że w tym miejscu należy wstrzyknąć zależność. Przekazujemy interfejs konstruktorowi ClientService
. Implementację tej usługi oznaczyliśmy wcześniej adnotacją @Service
, a teraz Spring będzie mógł przekazać instancję tej implementacji do konstruktora kontrolera. Następnie krok po kroku zaimplementujemy każdą metodę kontrolera do przetwarzania operacji CRUD. Zacznijmy od operacji Utwórz. Aby to zrobić, napiszmy metodę create
:
@PostMapping(value = "/clients")
public ResponseEntity<?> create(@RequestBody Client client) {
clientService.create(client);
return new ResponseEntity<>(HttpStatus.CREATED);
}
Przyjrzyjmy się tej metodzie: @PostMapping(value = "/clients")
- tutaj mamy na myśli, że metoda ta przetwarza żądania POST na adres /clients Metoda zwraca ResponseEntity<?>
. ResponseEntity
- specjalna klasa do zwracania odpowiedzi. Za jego pomocą możemy później zwrócić klientowi kod stanu HTTP. Metoda pobiera parametr @RequestBody Client client
, wartość tego parametru jest zastępowana z treści żądania. Streszczenie o tym mówi @RequestBody
. Wewnątrz treści metody wywołujemy metodę create na wcześniej utworzonej usłudze i przekazujemy ją kontrolerowi klienta zaakceptowanemu w parametrach. Następnie zwracamy status 201 Utworzono tworząc nowy obiekt ResponseEntity
i przekazując mu wymaganą wartość wyliczeniową HttpStatus
. Następnie realizujemy operację Read
: W pierwszej kolejności realizujemy operację uzyskania listy wszystkich dostępnych klientów:
@GetMapping(value = "/clients")
public ResponseEntity<List<Client>> read() {
final List<Client> clients = clientService.readAll();
return clients != null && !clients.isEmpty()
? new ResponseEntity<>(clients, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Zacznijmy analizę: @GetMapping(value = "/clients")
- wszystko jest podobnie jak w adnotacji @PostMapping
, tylko teraz przetwarzamy żądania GET. Tym razem wracamy ResponseEntity<List<Client>>
, tylko tym razem oprócz statusu HTTP zwrócimy także treść odpowiedzi, którą będzie lista klientów. W kontrolerach Spring REST wszystkie obiekty POJO, a także kolekcje obiektów POJO zwracane jako treści odpowiedzi, są automatycznie serializowane do formatu JSON, chyba że wyraźnie określono inaczej. To nam całkiem odpowiada. Wewnątrz metody, korzystając z naszej usługi, otrzymujemy listę wszystkich klientów. Następnie, jeśli lista nie jest pusta lub ma wartość null, za pomocą klasy zwracamy ResponseEntity
listę klientów i status HTTP 200 OK. W przeciwnym razie po prostu zwracamy status HTTP 404 Nie znaleziono. Następnie zaimplementujemy możliwość odbioru klienta po jego id:
@GetMapping(value = "/clients/{id}")
public ResponseEntity<Client> read(@PathVariable(name = "id") int id) {
final Client client = clientService.read(id);
return client != null
? new ResponseEntity<>(client, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Nowością jest to, że mamy teraz zmienną ścieżki. Zmienna zdefiniowana w identyfikatorze URI. value = "/clients/{id}"
. Zaznaczyliśmy to w nawiasach klamrowych. Natomiast w parametrach metody przyjmujemy ją jako int
zmienną korzystając z adnotacji @PathVariable(name = "id")
. Metoda ta akceptuje żądania uri w postaci /clients/{id}
, gdzie zamiast tego {id}
może występować dowolna wartość liczbowa. Wartość ta jest następnie przekazywana do zmiennej int id
— parametru metody. W ciele odbieramy przedmiot Client
korzystając z naszej usługi i akceptujemy go id
. I wtedy analogicznie do listy zwracamy albo status 200 OK i sam obiekt Client
, albo po prostu status 404 Not Found, jeśli id
w systemie nie ma klienta z tym. Pozostaje wdrożyć dwie operacje - Aktualizuj i Usuń. Oto kod tych metod:
@PutMapping(value = "/clients/{id}")
public ResponseEntity<?> update(@PathVariable(name = "id") int id, @RequestBody Client client) {
final boolean updated = clientService.update(client, id);
return updated
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
@DeleteMapping(value = "/clients/{id}")
public ResponseEntity<?> delete(@PathVariable(name = "id") int id) {
final boolean deleted = clientService.delete(id);
return deleted
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
W metodach tych nie ma w zasadzie nic nowego, dlatego pominiemy szczegółowy opis. Jedyną rzeczą wartą wspomnienia jest to, że metoda update
przetwarza żądania PUT (adnotacja @PutMapping
), a metoda delete
przetwarza żądania DELETE (adnotacja DeleteMapping
). Oto pełny kod kontrolera:
@RestController
public class ClientController {
private final ClientService clientService;
@Autowired
public ClientController(ClientService clientService) {
this.clientService = clientService;
}
@PostMapping(value = "/clients")
public ResponseEntity<?> create(@RequestBody Client client) {
clientService.create(client);
return new ResponseEntity<>(HttpStatus.CREATED);
}
@GetMapping(value = "/clients")
public ResponseEntity<List<Client>> read() {
final List<client> clients = clientService.readAll();
return clients != null && !clients.isEmpty()
? new ResponseEntity<>(clients, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
@GetMapping(value = "/clients/{id}")
public ResponseEntity<Client> read(@PathVariable(name = "id") int id) {
final Client client = clientService.read(id);
return client != null
? new ResponseEntity<>(client, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
@PutMapping(value = "/clients/{id}")
public ResponseEntity<?> update(@PathVariable(name = "id") int id, @RequestBody Client client) {
final boolean updated = clientService.update(client, id);
return updated
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
@DeleteMapping(value = "/clients/{id}")
public ResponseEntity<?> delete(@PathVariable(name = "id") int id) {
final boolean deleted = clientService.delete(id);
return deleted
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
}
W rezultacie struktura naszego projektu wygląda następująco:
Uruchom i przetestuj
Aby uruchomić naszą aplikację wystarczy uruchomić metodęmain
w klasie RestExampleApplication
. Aby przetestować usługi internetowe RESTful, musisz pobrać nowe oprogramowanie) Faktem jest, że żądania GET można dość łatwo wysłać ze zwykłej przeglądarki, ale w przypadku POST, PUT i DELETE zwykła przeglądarka nie wystarczy. Nie martw się: możesz używać Postmana do wysyłania dowolnych żądań HTTP. Możesz go pobrać stąd . Po pobraniu i zainstalowaniu przystępujemy do testowania naszej aplikacji. W tym celu otwórz program i utwórz nowe zgłoszenie: Kliknij przycisk Nowy w lewym górnym rogu. Następnie wybierz opcję Żądanie: Następnie nadaj mu nazwę i zapisz. Spróbujmy teraz wysłać żądanie POST do serwera i utworzyć pierwszego klienta: Tworzymy w ten sposób kilku klientów. Następnie zmieniamy typ żądania na GET i wysyłamy je do serwera:
Wyniki ogólne
Gratulacje: omówiliśmy całkiem sporo tematu REST. Cały materiał okazał się obszerny, ale mamy nadzieję, że będzie dla Ciebie przydatny:-
Dowiedzieliśmy się, czym jest REST.
-
Zapoznaliśmy się z historią REST.
-
Rozmawialiśmy o ograniczeniach i zasadach tego stylu architektonicznego:
- doprowadzenie architektury do modelu klient-serwer;
- brak kondycji;
- buforowanie;
- jednolitość interfejsu;
- warstwy;
- kod na żądanie (ograniczenie opcjonalne).
-
Sprawdziliśmy zalety, jakie zapewnia REST
-
Szczegółowo zbadaliśmy, w jaki sposób serwer i klient współdziałają ze sobą za pomocą protokołu HTTP.
-
Przyjrzyjmy się bliżej prośbom i odpowiedziom. Ich elementy zostały zdemontowane.
-
Na koniec przeszliśmy do ćwiczeń i napisaliśmy własną małą aplikację RESTful w Spring Boot. Dowiedzieliśmy się nawet, jak to przetestować za pomocą programu Postman.
Praca domowa
Spróbuj wykonać następujące czynności:- Kierując się powyższym opisem, utwórz własny projekt Spring Boot i zaimplementuj w nim tę samą logikę, co na wykładzie. Powtórz wszystko 1 w 1.
- Uruchom to. aplikacja.
- Pobierz i skonfiguruj Postmana (lub dowolne inne narzędzie do wysyłania żądań, nawet curl).
- Przetestuj żądania POST i GET w taki sam sposób, jak opisano w wykładzie.
- Przetestuj samodzielnie żądania PUT i DELETE.
GO TO FULL VERSION