זהו החלק האחרון של הסקירה הכללית שלנו על REST. בחלקים הקודמים, כיסינו:
לחץ על כפתור "הבא". בחלון הבא, ציין את "Maven Project" כסוג הפרויקט, ציין את "הקבוצה" ואת "Artifact":
לחץ על כפתור "הבא". בחלון הבא, עלינו לבחור את רכיבי Spring Framework הנחוצים לפרויקט. רשת אביב תספיק לנו:
לחץ על כפתור "הבא". כעת כל שנותר הוא לציין את שם הפרויקט ומיקומו במערכת הקבצים:
לחצו על כפתור "סיום". הפרויקט נוצר, ועכשיו אנחנו יכולים לראות את המבנה שלו: IDEA יצר עבורנו
מתאר פריסה של Maven (pom.xml) ואת המחלקה הראשית של האפליקציה ( ).
לחץ על כפתור "חדש" בפינה השמאלית העליונה. לאחר מכן, בחר "בקשה":
לאחר מכן, תן לו שם ושמור אותו. כעת ננסה לשלוח בקשת POST לשרת וליצור את הלקוח הראשון:
אנו יוצרים מספר לקוחות בצורה זו. לאחר מכן אנו משנים את סוג הבקשה ל-GET ושולחים את הבקשה לשרת:

יצירת פרויקט
בחלק זה, ניצור יישום קטן של RESTful באמצעות Spring Boot. האפליקציה שלנו תטמיע פעולות CRUD (צור, קרא, עדכן, מחק) על הלקוחות מהדוגמה בחלק הקודם של הסקירה. כדי להתחיל, ניצור אפליקציית Spring Boot חדשה דרך התפריט: File -> New -> Project... בחלון שנפתח בחר Spring Initializr וציין את Project SDK:




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.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);
}
}
יצירת פונקציונליות REST
האפליקציה שלנו היא מערכת לניהול לקוחות. לכן, הדבר הראשון שעלינו לעשות הוא ליצור ישות לקוח. זה יהיה מחלקה POJO (אובייקט ג'אווה ישן רגיל). צורmodel
חבילה בתוך com.codegym.lessons.rest_example
החבילה. בתוך model
החבילה, צור את 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;
}
}
השירות יישם פעולות CRUD על לקוחות. השלב הבא הוא יצירת שירות שיטמיע את הפעולות הללו. בחבילה com.codegym.lessons.rest_example
, צור service
חבילה. ובתוך זה, צור CustomerService
ממשק. הנה קוד הממשק עם הערות:
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);
}
לאחר מכן, עלינו ליישם את הממשק הזה. עכשיו א Map<Integer, Customer>
יהיה לאחסן את הלקוחות שלנו. מפתחות המפה יהיו מזהי הלקוח, והערכים יהיו הלקוחות עצמם. זה נעשה כדי לא להעמיס על דוגמה זו בפרטים הספציפיים של עבודה עם מסד נתונים אמיתי. עם זאת, בעתיד נוכל לכתוב יישום נוסף של הממשק, שיאפשר להתחבר למסד נתונים אמיתי. בחבילה service
, צור יישום של 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;
}
}
הביאור @Service
אומר לאביב שהשיעור הזה הוא שירות. זהו סוג מיוחד של מחלקה המיישמת לוגיקה של יישומים עסקיים. לאחר מכן, הודות להערה זו, ספרינג תשתמש בהזרקת תלות כדי לספק לנו מופע של מחלקה זו בכל המקומות שבהם הוא נחוץ. עכשיו הגיע הזמן ליצור בקר. זהו מחלקה מיוחדת שבה ניישם את ההיגיון לעיבוד בקשות לקוח הנשלחות לנקודות קצה (URI). כדי להבהיר את כל זה, ניצור את המחלקה הזו בהדרגה. ראשית, צור את המחלקה עצמה והוסף תלות ב CustomerService
:
@RestController
public class CustomerController {
private final CustomerService customerService;
@Autowired
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
}
בואו נסביר את ההערות: @RestController אומר ל-Spring שהמחלקה הזו היא בקר REST. במילים אחרות, מחלקה זו מיישמת את ההיגיון לעיבוד בקשות לקוח. @Autowired אומר ל-Spring שצריך להוסיף כאן תלות. אנחנו מעבירים את CustomerService
הממשק לבנאי. קודם לכן, סימנו את היישום של שירות זה עם @Service
ההערה, וכעת Spring יוכל להעביר מופע של יישום זה לבנאי הבקר. לאחר מכן, ניישם כל שיטת בקר לטיפול בפעולות CRUD. נתחיל עם פעולת היצירה. לשם כך נכתוב create
שיטה:
@PostMapping(value = "/customers")
public ResponseEntity<?> create(@RequestBody Customer customer) {
customerService.create(customer);
return new ResponseEntity<>(HttpStatus.CREATED);
}
בואו ננתח את השיטה הזו: @PostMapping(value = "/customers")
מתכוון ששיטה זו מעבדת בקשות POST הנשלחות לכתובת "/customers". השיטה מחזירה א ResponseEntity<?>
. A ResponseEntity
הוא מחלקה מיוחדת להחזרת תגובות. מאוחר יותר, נשתמש בו כדי להחזיר קוד סטטוס HTTP ללקוח. לשיטה יש @RequestBody Customer customer
פרמטר. הערך של פרמטר זה מגיע מגוף הבקשה. ההערה @RequestBody
מעידה על כך. בתוך גוף השיטה, אנו קוראים למתודה create()
על השירות שנוצר בעבר ומעבירים לה את בקר הלקוח שקיבל בפרמטרים. לאחר מכן נחזיר את הסטטוס "201 נוצר" על ידי יצירת ResponseEntity
אובייקט חדש והעברת HttpStatus
שדה ה-enum המתאים אליו. לאחר מכן, ניישם את read
הפעולה: ראשית, ניישם את הפעולה כדי לקבל רשימה של כל הלקוחות הזמינים:
@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")
- הכל כאן דומה להערה @PostMapping
, אבל עכשיו אנחנו מעבדים בקשות GET. הפעם נחזיר ResponseEntity<List<Customer>>
, ובנוסף לסטטוס HTTP נחזיר גם גוף תגובה שיהיה רשימת הלקוחות. בבקרי REST של Spring, הכל הוא אובייקטי POJO ואוספים של אובייקטי POJO, המוחזרים כגופי תגובה ומוחזרים באופן אוטומטי ל-JSON, אלא אם צוין אחרת. זה מתאים לנו בצורה מושלמת. בתוך השיטה, אנו משתמשים בשירות שלנו כדי לקבל רשימה של כל הלקוחות. לאחר מכן, אם הרשימה אינה אפסית ואינה ריקה, אנו משתמשים במחלקה ResponseEntity
כדי להחזיר את רשימת הלקוחות ואת קוד הסטטוס "200 OK" HTTP. אחרת, אנו פשוט מחזירים את קוד סטטוס ה-HTTP "404 לא נמצא". כעת ניישם את היכולת להשיג לקוח באמצעות המזהה שלו:
@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);
}
דבר אחד חדש כאן הוא משתנה הנתיב. המשתנה מוגדר ב-URI: value = "/customers/{id}"
. אנו מציינים זאת בפלטה מתולתלת. ואנחנו מקבלים את זה כפרמטר int
שיטה באמצעות @PathVariable(name = "id")
ההערה. שיטה זו תקבל בקשות הנשלחות ל-URI בצורה /customers/{id}
, כאשר {id}
היא מייצגת כל ערך מספרי. ערך זה מועבר לאחר מכן דרך int id
המשתנה לפרמטר השיטה. בגוף, אנו מקבלים את Customer
החפץ באמצעות השירות שלנו ואת המתקבל id
. ואז, באנלוגיה לרשימה, אנו מחזירים את סטטוס "200 בסדר" ואת האובייקט Customer
עצמו, או פשוט את סטטוס "404 לא נמצא" אם למערכת אין לקוח עם זה id
. אנחנו עדיין צריכים ליישם שתי פעולות: עדכון ומחיקה. להלן הקוד לשיטות אלו:
@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);
}
אין שום דבר חדש במהותו בשיטות הללו, אז נדלג על התיאור המפורט. הדבר היחיד שכדאי להזכיר הוא שהשיטה update()
מטפלת בבקשות PUT ( @PutMapping
ביאור), והשיטה delete()
מטפלת בבקשות DELETE ( DeleteMapping
ביאור). הנה הקוד המלא לבקר:
@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);
}
}
כתוצאה מכך, מבנה הפרויקט שלנו הוא כדלקמן: 
השקה ובדיקה
כדי להפעיל את האפליקציה שלנו, פשוט הפעל אתmain()
השיטה בכיתה RestExampleApplication
. אבל כדי לבדוק שירותי אינטרנט של RESTful, עלינו להוריד תוכנה נוספת. העובדה היא שבקשות GET הן די פשוטות לשליחה מדפדפן רגיל, אבל דפדפן רגיל לא יכול לשלוח בקשות POST, PUT ו-DELETE. אל תדאג: אתה יכול להשתמש בתוכנה בשם Postman כדי לשלוח כל בקשות HTTP. אתה יכול להוריד אותו כאן
. לאחר הורדה והתקנה של Postman, אנו מתחילים לבדוק את האפליקציה שלנו. לשם כך, פתח את התוכנית וצור בקשה חדשה: 



סיכום
מזל טוב! כיסינו מספיק את REST. היה נפח גדול של חומר, אבל אני מקווה שזה היה שימושי עבורך:-
למדנו מה זה REST.
-
למדנו כיצד REST נוצר.
-
דיברנו על המגבלות והעקרונות מאחורי הסגנון האדריכלי הזה:
- ארכיטקטורת שרת-לקוח
- חסר מדינה
- שמירה במטמון
- ממשק אחיד
- שכבות
- קוד לפי דרישה (אופציונלי)
-
בדקנו את היתרונות שמספק REST
-
בדקנו בפירוט כיצד השרת והלקוח מתקשרים זה עם זה באמצעות פרוטוקול HTTP.
-
בדקנו מקרוב את הבקשות והתגובות. ניתחנו את החלקים המרכיבים שלהם.
-
לבסוף, קיבלנו קצת ניסיון מעשי על ידי כתיבת יישום RESTful קטן משלנו באמצעות Spring Boot. ואפילו למדנו איך לבדוק את זה באמצעות Postman.
שיעורי בית
נסה את הפעולות הבאות:- בעקבות התיאור שלמעלה, צור פרויקט Spring Boot משלך והטמיע את אותו היגיון כמו בשיעור. חזור על הכל בדיוק.
- הפעל את האפליקציה.
- הורד והגדר את Postman (או כל כלי אחר לשליחת בקשות, למשל, curl).
- בדוק בקשות POST ו-GET באותו אופן המתואר בשיעור.
- בדוק בעצמך בקשות PUT ו-DELETE.
GO TO FULL VERSION