זהו החלק האחרון של ניתוח REST. בחלקים הקודמים:
יצירת פרויקט
בחלק זה, ניצור יישום קטן של RESTful באמצעות Spring Boot. האפליקציה שלנו תטמיע פעולות CRUD (צור, קרא, עדכן, מחק) על לקוחות מהדוגמה מהחלק האחרון של הניתוח. ראשית, בואו ניצור אפליקציית Spring Boot חדשה דרך התפריט File -> New -> Project... בחלון שנפתח, בחר Spring Initializr וציין Project SDK: לחץ על כפתור Next. בחלון הבא, ציין את סוג הפרויקט של Maven, ציין קבוצה ו-Artifact: לחץ על הלחצן הבא. בחלון הבא, עלינו לבחור את רכיבי Spring Framework הדרושים לפרויקט. אינטרנט אביב יספיק לנו: לחץ על הלחצן הבא. לאחר מכן, כל שנותר הוא לציין את שם הפרויקט ומיקומו במערכת הקבצים: לחצו על כפתור סיום. הפרויקט נוצר, כעת אנו יכולים לראות את המבנה שלו: IDEA יצרה עבורנו את מתאר פריסת מערכת ה-Maven build - pom.xml ואת מחלקת היישומים הראשית:RestExampleApplication
. הנה הקוד שלהם:
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>
RestExampleApplication:
@SpringBootApplication
public class RestExampleApplication {
public static void main(String[] args) {
SpringApplication.run(RestExampleApplication.class, args);
}
}
יצירת פונקציונליות REST
האפליקציה שלנו מנהלת לקוחות. אז הדבר הראשון שעלינו לעשות הוא ליצור ישות לקוחה. זה יהיה שיעור POJO. בואו ניצור חבילהmodel
בתוך חבילה com.javarush.lectures.rest_example
. model
בואו ניצור מחלקה בתוך החבילה 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;
}
}
השירות יישם פעולות CRUD על הלקוח. השלב הבא הוא יצירת שירות שיטמיע את הפעולות הללו. בחבילה com.javarush.lectures.rest_example
ניצור חבילה service
שבתוכה ניצור ממשק ClientService
. הנה קוד הממשק עם הערות:
public interface ClientService {
/**
* Создает нового клиента
* @param client - клиент для создания
*/
void create(Client client);
/**
* returns список всех имеющихся клиентов
* @return список клиентов
*/
List<client> readAll();
/**
* returns клиента по его ID
* @param id - ID клиента
* @return - an object клиента с заданным 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);
}
בשלב הבא עלינו ליצור יישום של ממשק זה. כעת הוא יפעל כמאגר לקוחות Map<Integer, Client>
. מפתח הכרטיס יהיה מזהה הלקוח, והערך יהיה הלקוח עצמו. זה נעשה כדי לא להעמיס על הדוגמה בפרטי העבודה עם מסד הנתונים. עם זאת, בעתיד נוכל לכתוב יישום נוסף של הממשק בו ניתן יהיה לחבר בסיס נתונים אמיתי. בחבילה service
ניצור יישום של הממשק ClientService
:
@Service
public class ClientServiceImpl implements ClientService {
// Хранorще клиентов
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;
}
}
ההערה @Service
אומרת לאביב שהשיעור הזה הוא שירות. זהו סוג מיוחד של מחלקה שבה מיושמת היגיון עסקי כלשהו של האפליקציה. לאחר מכן, הודות להערה זו, Spring תספק לנו מופע של מחלקה זו במקומות שבהם הוא נחוץ באמצעות Dependency Injection. עכשיו הגיע הזמן ליצור את הבקר. מחלקה מיוחדת בה אנו מיישמים את ההיגיון לעיבוד בקשות לקוח עבור נקודות קצה (URI). כדי להבהיר את זה, ניצור את המחלקה הזו בחלקים. ראשית, בואו ניצור את המחלקה עצמה ונציג בה תלות ClientService
:
@RestController
public class ClientController {
private final ClientService clientService;
@Autowired
public ClientController(ClientService clientService) {
this.clientService = clientService;
}
}
בואו נבהיר את ההערות: @RestController - אומר ל-Spring שהמחלקה הזו היא בקר REST. הָהֵן. מחלקה זו תטמיע את ההיגיון לעיבוד בקשות לקוח @Autowired - אומר ל-Spring שצריך להחדיר תלות במקום הזה. אנו מעבירים את הממשק לבנאי ClientService
. סימנו את היישום של שירות זה עם הערה @Service
קודם לכן, וכעת Spring יוכל להעביר מופע של יישום זה לבנאי הבקר. בשלב הבא ניישם כל שיטת בקר לעיבוד פעולות CRUD, צעד אחר צעד. נתחיל בפעולת היצירה. לשם כך, בוא נכתוב שיטה create
:
@PostMapping(value = "/clients")
public ResponseEntity<?> create(@RequestBody Client client) {
clientService.create(client);
return new ResponseEntity<>(HttpStatus.CREATED);
}
בואו נסתכל על השיטה הזו: @PostMapping(value = "/clients")
- כאן אנחנו מתכוונים ששיטה זו מעבדת בקשות POST לכתובת /clients השיטה מחזירה ResponseEntity<?>
. ResponseEntity
- שיעור מיוחד להחזרת תשובות. באמצעותו נוכל להחזיר מאוחר יותר את קוד סטטוס ה-HTTP ללקוח. השיטה לוקחת פרמטר @RequestBody Client client
, הערך של פרמטר זה מוחלף מגוף הבקשה. התקציר מדבר על זה @RequestBody
. בתוך גוף השיטה, אנו קוראים לשיטת create בשירות שנוצר קודם לכן ומעבירים לה את בקר הלקוח המקובל בפרמטרים. לאחר מכן אנו מחזירים את הסטטוס 201 נוצר על ידי יצירת אובייקט חדש ResponseEntity
והעברת ערך ה-enum הנדרש אליו HttpStatus
. לאחר מכן, אנו מיישמים את הפעולה Read
: ראשית, אנו מיישמים את פעולת השגת רשימה של כל הלקוחות הזמינים:
@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")
- הכל דומה להערה @PostMapping
, רק עכשיו אנחנו מעבדים בקשות GET. הפעם נחזיר ResponseEntity<List<Client>>
, רק שהפעם בנוסף לסטטוס HTTP נחזיר גם את גוף התגובה שיהיה רשימת לקוחות. בבקרי Spring REST, כל אובייקטי POJO, כמו גם אוספים של אובייקטי POJO המוחזרים כגופי תגובה, עוברים באופן אוטומטי בסידרה ל-JSON אלא אם צוין אחרת במפורש. אנחנו די מרוצים מזה. בתוך השיטה, באמצעות השירות שלנו, אנו מקבלים רשימה של כל הלקוחות. לאחר מכן, אם הרשימה אינה אפסית או ריקה, אנו מחזירים ResponseEntity
את רשימת הלקוחות ואת מצב ה-HTTP 200 אישור באמצעות המחלקה. אחרת, אנחנו פשוט מחזירים את סטטוס HTTP 404 לא נמצא. לאחר מכן, ניישם את היכולת להשיג לקוח לפי המזהה שלו:
@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);
}
משהו חדש הוא שכעת יש לנו משתנה נתיב. משתנה המוגדר ב-URI. value = "/clients/{id}"
. ציינו זאת בפלטה מתולתלת. ובפרמטרים של השיטה אנו מקבלים אותו כמשתנה int
באמצעות ההערה @PathVariable(name = "id")
. שיטה זו תקבל בקשות עבור uri של הטופס /clients/{id}
, כאשר במקום זאת {id}
יכול להיות כל ערך מספרי. ערך זה מועבר לאחר מכן למשתנה int id
- פרמטר השיטה. בגוף אנו מקבלים את החפץ Client
באמצעות השירות שלנו ומתקבל id
. ואז, באנלוגיה לרשימה, אנו מחזירים או את מצב 200 אישור ואת האובייקט עצמו Client
, או פשוט את סטטוס 404 לא נמצא, אם אין id
לקוח עם זה במערכת. נותר ליישם שתי פעולות - עדכון ומחיקה. להלן הקוד לשיטות אלו:
@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);
}
אין שום דבר חדש במהותו בשיטות אלו, אז נדלג על תיאור מפורט. הדבר היחיד שכדאי להזכיר הוא שהשיטה update
מעבדת בקשות PUT (ביאור @PutMapping
), והשיטה delete
מעבדת בקשות DELETE (ביאור DeleteMapping
). להלן קוד הבקר המלא:
@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);
}
}
כתוצאה מכך, המבנה של הפרויקט שלנו נראה כך:
השקה ובדיקה
כדי להפעיל את האפליקציה שלנו, פשוט הפעל את השיטהmain
במחלקה RestExampleApplication
. וכדי לבדוק שירותי אינטרנט של RESTful, צריך להוריד תוכנה חדשה) העובדה היא שבקשות GET די קלות לשלוח מדפדפן רגיל, אבל עבור POST, PUT ו-DELETE לא מספיק דפדפן רגיל. אל תדאג: אתה יכול להשתמש ב-Postman כדי לשלוח כל בקשות HTTP. אתה יכול להוריד אותו מכאן . לאחר ההורדה וההתקנה, אנו מתחילים לבדוק את האפליקציה שלנו. לשם כך, פתח את התוכנית וצור בקשה חדשה: לחץ על כפתור חדש בפינה השמאלית העליונה. לאחר מכן, בחר בקש: הבא, תן לו שם ושמור אותו. כעת ננסה לשלוח בקשת POST לשרת וליצור את הלקוח הראשון: אנו יוצרים מספר לקוחות בצורה זו. לאחר מכן אנו משנים את סוג הבקשה ל-GET ושולחים אותו לשרת:
תוצאות כלליות
מזל טוב: כיסינו לא מעט את הנושא של REST. כל החומר התברר כנפח, אבל אנו מקווים שהוא יהיה שימושי עבורך:-
למדנו מה זה REST.
-
התוודענו להיסטוריה של REST.
-
דיברנו על המגבלות והעקרונות של סגנון אדריכלי זה:
- הבאת הארכיטקטורה למודל שרת-לקוח;
- חוסר מצב;
- מטמון;
- אחידות הממשק;
- שכבות;
- קוד לפי דרישה (הגבלה אופציונלית).
-
בדקנו את היתרונות שמספקת REST
-
בדקנו בפירוט כיצד השרת והלקוח מתקשרים זה עם זה באמצעות פרוטוקול HTTP.
-
בואו נסתכל מקרוב על הבקשות והתגובות. הרכיבים שלהם פורקו.
-
לבסוף, עברנו לתרגול וכתבנו יישום קטן משלנו RESTful ב-Spring Boot. ואפילו למדנו איך לבדוק את זה באמצעות תוכנית Postman.
שיעורי בית
נסה את הפעולות הבאות:- בעקבות התיאור למעלה, צור פרויקט Spring Boot משלך והטמיע בו את אותו ההיגיון כמו בהרצאה. חזור על הכל 1 ב-1.
- הפעל אותו. יישום.
- הורד והגדר את Postman (או כל כלי אחר לשליחת בקשות, אפילו סלסול).
- בדוק בקשות POST ו-GET באותו אופן כפי שמתואר בהרצאה.
- בדוק בעצמך בקשות PUT ו-DELETE.
GO TO FULL VERSION