CodeGym /בלוג Java /Random-HE /סקירה כללית של REST. חלק 3: בניית שירות RESTful על Spring...
John Squirrels
רָמָה
San Francisco

סקירה כללית של REST. חלק 3: בניית שירות RESTful על Spring Boot

פורסם בקבוצה
זהו החלק האחרון של הסקירה הכללית שלנו על REST. בחלקים הקודמים, כיסינו: סקירה כללית של REST.  חלק 3: בניית שירות RESTful על Spring Boot - 1

יצירת פרויקט

בחלק זה, ניצור יישום קטן של RESTful באמצעות Spring Boot. האפליקציה שלנו תטמיע פעולות CRUD (צור, קרא, עדכן, מחק) על הלקוחות מהדוגמה בחלק הקודם של הסקירה. כדי להתחיל, ניצור אפליקציית Spring Boot חדשה דרך התפריט: File -> New -> Project... בחלון שנפתח בחר Spring Initializr וציין את Project SDK: סקירה כללית של REST.  חלק 3: בניית שירות RESTful ב-Spring Boot - 2לחץ על כפתור "הבא". בחלון הבא, ציין את "Maven Project" כסוג הפרויקט, ציין את "הקבוצה" ואת "Artifact": סקירה כללית של REST.  חלק 3: בניית שירות RESTful על Spring Boot - 3לחץ על כפתור "הבא". בחלון הבא, עלינו לבחור את רכיבי Spring Framework הנחוצים לפרויקט. רשת אביב תספיק לנו: סקירה כללית של REST.  חלק 3: בניית שירות RESTful על Spring Boot - 4לחץ על כפתור "הבא". כעת כל שנותר הוא לציין את שם הפרויקט ומיקומו במערכת הקבצים: סקירה כללית של REST.  חלק 3: בניית שירות RESTful על Spring Boot - 5לחצו על כפתור "סיום". הפרויקט נוצר, ועכשיו אנחנו יכולים לראות את המבנה שלו: IDEA יצר עבורנו סקירה כללית של REST.  חלק 3: בניית שירות RESTful על Spring Boot - 6מתאר פריסה של Maven (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.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);
   }
}
כתוצאה מכך, מבנה הפרויקט שלנו הוא כדלקמן: סקירה כללית של REST.  חלק 3: בניית שירות RESTful על Spring Boot - 7

השקה ובדיקה

כדי להפעיל את האפליקציה שלנו, פשוט הפעל את main()השיטה בכיתה RestExampleApplication. אבל כדי לבדוק שירותי אינטרנט של RESTful, עלינו להוריד תוכנה נוספת. העובדה היא שבקשות GET הן די פשוטות לשליחה מדפדפן רגיל, אבל דפדפן רגיל לא יכול לשלוח בקשות POST, PUT ו-DELETE. אל תדאג: אתה יכול להשתמש בתוכנה בשם Postman כדי לשלוח כל בקשות HTTP. אתה יכול להוריד אותו כאן . לאחר הורדה והתקנה של Postman, אנו מתחילים לבדוק את האפליקציה שלנו. לשם כך, פתח את התוכנית וצור בקשה חדשה: סקירה כללית של REST.  חלק 3: בניית שירות RESTful ב-Spring Boot - 9לחץ על כפתור "חדש" בפינה השמאלית העליונה. לאחר מכן, בחר "בקשה": סקירה כללית של REST.  חלק 3: בניית שירות RESTful על Spring Boot - 10לאחר מכן, תן לו שם ושמור אותו. כעת ננסה לשלוח בקשת POST לשרת וליצור את הלקוח הראשון: סקירה כללית של REST.  חלק 3: בניית שירות RESTful ב-Spring Boot - 11אנו יוצרים מספר לקוחות בצורה זו. לאחר מכן אנו משנים את סוג הבקשה ל-GET ושולחים את הבקשה לשרת: סקירה כללית של REST.  חלק 3: בניית שירות RESTful ב-Spring Boot - 12

סיכום

מזל טוב! כיסינו מספיק את REST. היה נפח גדול של חומר, אבל אני מקווה שזה היה שימושי עבורך:
  1. למדנו מה זה REST.

  2. למדנו כיצד REST נוצר.

  3. דיברנו על המגבלות והעקרונות מאחורי הסגנון האדריכלי הזה:

    • ארכיטקטורת שרת-לקוח
    • חסר מדינה
    • שמירה במטמון
    • ממשק אחיד
    • שכבות
    • קוד לפי דרישה (אופציונלי)
  4. בדקנו את היתרונות שמספק REST

  5. בדקנו בפירוט כיצד השרת והלקוח מתקשרים זה עם זה באמצעות פרוטוקול HTTP.

  6. בדקנו מקרוב את הבקשות והתגובות. ניתחנו את החלקים המרכיבים שלהם.

  7. לבסוף, קיבלנו קצת ניסיון מעשי על ידי כתיבת יישום RESTful קטן משלנו באמצעות Spring Boot. ואפילו למדנו איך לבדוק את זה באמצעות Postman.

פיו. זה היה הרבה, אבל עדיין יש לך משהו לעשות בתור שיעורי בית.

שיעורי בית

נסה את הפעולות הבאות:
  1. בעקבות התיאור שלמעלה, צור פרויקט Spring Boot משלך והטמיע את אותו היגיון כמו בשיעור. חזור על הכל בדיוק.
  2. הפעל את האפליקציה.
  3. הורד והגדר את Postman (או כל כלי אחר לשליחת בקשות, למשל, curl).
  4. בדוק בקשות POST ו-GET באותו אופן המתואר בשיעור.
  5. בדוק בעצמך בקשות PUT ו-DELETE.
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION