JavaRush /בלוג Java /Random-HE /REST API ומשימת בדיקה נוספת.
Денис
רָמָה
Киев

REST API ומשימת בדיקה נוספת.

פורסם בקבוצה
חלק א': התחלה מאיפה להתחיל? באופן מוזר, אבל מהמפרט הטכני. חשוב ביותר לוודא כי לאחר קריאת ה-TOR שהוגשה אתם מבינים היטב מה כתוב בו ולמה מצפה הלקוח. ראשית, זה חשוב להמשך ההטמעה, ושנית, אם לא תממש את המצופה ממך, זה לא יהיה לטובתך. כדי להימנע מבזבוז אוויר, בואו נשרטט מפרט טכני פשוט. לכן, אני רוצה שירות שאליו אוכל לשלוח נתונים, הם יאוחסנו בשירות ויוחזרו אלי כרצוני. אני גם צריך להיות מסוגל לעדכן ולמחוק את הנתונים האלה במידת הצורך . כמה משפטים לא נראים כמו דבר ברור, נכון? איך אני רוצה לשלוח נתונים לשם? באילו טכנולוגיות להשתמש? באיזה פורמט הנתונים האלה יהיו? אין גם דוגמאות לנתונים נכנסים ויוצאים. מסקנה - המפרט הטכני כבר גרוע . בואו ננסה לנסח מחדש: אנחנו צריכים שירות שיכול לעבד בקשות HTTP ולעבוד עם נתונים מועברים. זה יהיה מסד הנתונים של רישומי כוח אדם. יהיו לנו עובדים, הם מחולקים לפי מחלקות והתמחויות, ייתכן שיוטלו עליהם משימות לעובדים. המשימה שלנו היא לבצע אוטומציה של תהליך הנהלת החשבונות של עובדים שנשכרו, מפוטרים, מועברים, וכן את תהליך ההקצאה והביטול של משימות באמצעות REST API. כשלב 1, אנו עובדים כעת רק עם עובדים. לשירות חייבות להיות מספר נקודות קצה כדי לעבוד איתו: - POST /employee - בקשת POST, אשר חייבת לקבל אובייקט JSON עם נתונים על העובד. יש לשמור אובייקט זה במסד הנתונים, אם אובייקט כזה כבר קיים במסד הנתונים, יש לעדכן את המידע בשדות בנתונים חדשים. - GET /employee - בקשת GET שמחזירה את כל רשימת העובדים השמורה במסד הנתונים - DELETE - DELETE /employee למחיקת עובד ספציפי מודל נתוני עובד:
{
  "firstName": String,
  "lastName": String,
  "department": String,
  "salary": String
  "hired": String //"yyyy-mm-dd"
  "tasks": [
  	//List of tasks, not needed for Phase 1
  ]
}
חלק ב': כלים לתפקיד אז, היקף העבודה פחות או יותר ברור, אבל איך אנחנו הולכים לעשות את זה? ברור, משימות כאלה במבחן ניתנות עם כמה מטרות יישום, לראות איך אתה מקודד, לאלץ אותך להשתמש ב-Spring ולעבוד קצת עם מסד הנתונים. ובכן, בוא נעשה את זה. אנחנו צריכים פרויקט SpringBoot עם תמיכת REST API ומסד נתונים. באתר https://start.spring.io/ תוכלו למצוא את כל מה שצריך. REST API או משימת בדיקה אחרת.  - 1 אתה יכול לבחור את מערכת הבנייה, השפה, גרסת SpringBoot, להגדיר הגדרות חפץ, גרסת Java ותלות. לחיצה על כפתור הוסף תלות תביא לתפריט אופייני עם שורת חיפוש. המועמדים הראשונים למילים מנוחה ונתונים הם Spring Web ו-Spring Data - נוסיף אותם. Lombok היא ספרייה נוחה המאפשרת לך להשתמש בהערות כדי להיפטר מקילומטרים של קוד עם שיטות getter ו-seter. בלחיצה על כפתור Generate נקבל ארכיון עם הפרוייקט אותו ניתן כבר לפרק ולפתוח ב-IDE המועדף עלינו. כברירת מחדל, נקבל פרויקט ריק, עם קובץ קונפיגורציה למערכת ה-build (במקרה שלי זה יהיה מדורג, אבל עם Maven אין הבדל מהותי, וקובץ הפעלה אביבי אחד) REST API או משימת בדיקה אחרת.  - 2 אנשים קשובים יכלו לשים לב לשני דברים . ראשית, יש לי שני קבצי הגדרות application.properties ו- application.yml. כברירת מחדל, תקבלו בדיוק מאפיינים - קובץ ריק בו תוכלו לאחסן הגדרות, אבל לי הפורמט של yml נראה קצת יותר קריא, עכשיו אראה השוואה: REST API או משימת בדיקה אחרת.  - 3 למרות שהתמונה משמאל נראית קומפקטית יותר , קל לראות כמות גדולה של כפילות בנתיב המאפיינים. התמונה מימין היא קובץ yml רגיל עם מבנה עץ די קל לקריאה. אשתמש בקובץ זה בהמשך הפרויקט. הדבר השני שאנשים קשובים עשויים לשים לב אליו הוא שלפרויקט שלי יש כבר כמה חבילות. אין שם עדיין קוד שפוי, אבל שווה לעבור עליהם. כיצד נכתבת בקשה? כשיש לנו משימה ספציפית, עלינו לפרק אותה - לפרק אותה לתת-משימות קטנות ולהתחיל ביישום עקבי שלהן. מה נדרש מאיתנו? אנחנו צריכים לספק API שהלקוח יכול להשתמש בו; התוכן של חבילת הבקר יהיה אחראי על החלק הזה של הפונקציונליות. החלק השני של האפליקציה הוא בסיס הנתונים - חבילת ה-persistence. בו נאחסן דברים כמו Database Entities (Entities) וכן Repositories - ממשקי קפיצים מיוחדים המאפשרים לך אינטראקציה עם מסד הנתונים. חבילת השירות תכיל שיעורי שירות. נדבר על מה זה סוג שירות האביב להלן. ואחרון חביב, חבילת utils. יאוחסנו שם שיעורים תועלתניים עם כל מיני שיטות עזר, למשל שיעורים לעבודה עם תאריך ושעה, או שיעורים לעבודה עם מחרוזות, ומי יודע מה עוד. בואו נתחיל ליישם את החלק הראשון של הפונקציונליות. חלק שלישי: בקר
@RestController
@RequestMapping("${application.endpoint.root}")
@RequiredArgsConstructor
public class EmployeeController {

    private final EmployeeService employeeService;

    @GetMapping("${application.endpoint.employee}")
    public ResponseEntity<List<Employee>> getEmployees() {
        return ResponseEntity.ok().body(employeeService.getAllEmployees());
    }
}
כעת מעמד ה-EmployeeController שלנו נראה כך. יש כאן מספר דברים חשובים שכדאי לשים אליהם לב. 1. הערות מעל המחלקה, @RestController הראשון אומר לאפליקציה שלנו שהמחלקה הזו תהיה נקודת קצה. 2. @RequestMapping, אם כי לא חובה, הוא הערה שימושית, היא מאפשרת לך להגדיר נתיב ספציפי לנקודת הקצה. הָהֵן. כדי לדפוק עליו, תצטרך לשלוח בקשות לא ל-localhost:port/employee, אלא במקרה זה ל-localhost:8086/api/v1/employee. למעשה, מאיפה הגיעו ה-API/v1 והעובד האלה? מאת application.yml שלנו אם תסתכל מקרוב, תוכל למצוא שם את השורות הבאות:
application:
  endpoint:
    root: api/v1
    employee: employee
    task: task
כפי שאתה יכול לראות, יש לנו משתנים כמו application.endpoint.root ו-application.endpoint.employee, אלה בדיוק מה שכתבתי בהערות, אני ממליץ לזכור את השיטה הזו - זה יחסוך הרבה זמן על הרחבה או שכתוב של פונקציונליות - זה תמיד נוח יותר להחזיק הכל בתצורה, ולא להקשיח את כל הפרוייקט. 3. @RequiredArgsConstructor הוא הערת Lombok, ספרייה נוחה המאפשרת להימנע מכתיבת דברים מיותרים. במקרה זה, ההערה שווה לעובדה שלכיתה יהיה בנאי ציבורי עם כל השדות מסומנים כסופיים
public EmployeeController(EmployeeService employeeService) {
    this.employeeService=employeeService;
}
אבל למה שנכתוב דבר כזה אם די בביאור אחד? :) אגב, מזל טוב, שדה הגמר הפרטי ביותר הזה הוא לא יותר מ-Dependency Injection הידוע לשמצה. בוא נמשיך הלאה, בעצם, איזה סוג של תחום הוא EmployeeService? זה יהיה אחד השירותים בפרויקט שלנו שיעבדו בקשות לנקודת קצה זו. הרעיון כאן הוא מאוד פשוט. לכל מחלקה צריכה להיות משימה משלה ואין להעמיס עליה בפעולות מיותרות. אם מדובר בבקר, תן לו לטפל בקבלת בקשות ושליחת תגובות, אך אנו מעדיפים להפקיד את העיבוד בשירות נוסף. הדבר האחרון שנותר במעמד זה הוא השיטה היחידה שמחזירה רשימה של כל עובדי החברה שלנו המשתמשים בשירות הנ"ל. הרשימה עצמה עטופה בישות בשם ResponseEntity. אני עושה זאת כדי שבעתיד, במידת הצורך, אוכל להחזיר בקלות את קוד התגובה וההודעה שאני צריך, שהמערכת האוטומטית תוכל להבין. אז, למשל, ResponseEntity.ok() יחזיר את הקוד 200, שיגיד שהכל בסדר, אבל אם אני מחזיר, למשל
return ResponseEntity.badRequest().body(Collections.emptyList());
לאחר מכן הלקוח יקבל קוד 400 - בקשה שגויה ורשימה ריקה בתגובה. בדרך כלל קוד זה מוחזר אם הבקשה אינה נכונה. אבל בקר אחד לא יספיק לנו כדי להפעיל את האפליקציה. התלות שלנו לא תאפשר לנו לעשות את זה, כי אנחנו עדיין צריכים שיהיה לנו בסיס :) ובכן, בואו נעבור לחלק הבא. חלק רביעי: התמדה פשוטה מכיוון שהמשימה העיקרית שלנו היא להשיק את האפליקציה, נגביל את עצמנו לכמה קטעים לעת עתה. כבר ראיתם במחלקת Controller שאנחנו מחזירים רשימה של אובייקטים מסוג Employee, זו תהיה הישות שלנו למסד הנתונים. בואו ניצור אותו בחבילת demo.persistence.entity . בעתיד ניתן יהיה להשלים את חבילת הישות עם ישויות אחרות ממסד הנתונים.
@Entity
@Data
@Accessors(chain = true)
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
}
זוהי מחלקה פשוטה כמו דלת, שהביאורים שלה אומרים בדיוק את הדברים הבאים: זוהי ישות מסד נתונים @Entity, זו מחלקה עם נתונים @Data - Lombok. הלומבו המועיל תיצור עבורנו את כל הגטרים, המגדירים, הקונסטרוקטורים הדרושים - מלית מלאה. ובכן, דובדבן קטן על העוגה הוא @Accessors(chain = true) למעשה, זהו יישום נסתר של דפוס ה-Builder. נניח שיש לך מחלקה עם חבורה של שדות שאתה רוצה להקצות לא דרך הבנאי, אלא לפי שיטות. בסדר שונה, אולי לא הכל בו זמנית. אתה אף פעם לא יודע איזה סוג של הגיון יהיה ביישום שלך. הערה זו היא המפתח שלך למשימה זו. בואו נראה:
public Employee createEmployee() {
    return new Employee().setName("Peter")
        				.setAge("28")
        				.setDepartment("IT");
}
בוא נניח שיש לנו את כל השדות האלה בכיתה שלנו😄 אפשר להקצות אותם, אי אפשר להקצות אותם, אפשר לערבב אותם במקומות. במקרה של 3 נכסים בלבד, זה לא נראה כמו משהו יוצא מן הכלל. אבל יש מחלקות עם מספר הרבה יותר גדול של מאפיינים, למשל 50. ולכתוב משהו כמו
public Employee createEmployee() {
    return new Employee("Peter", "28", "IT", "single", "loyal", List.of(new Task("do Something 1"), new Task ("do Something 2")));
}
לא נראה יפה במיוחד, נכון? אנחנו גם צריכים לעקוב בקפדנות אחר סדר הוספת המשתנים בהתאם לבנאי. עם זאת, אני סוטה, בוא נחזור לנקודה. כעת יש לנו שדה אחד (חובה) בו - מזהה ייחודי. במקרה זה, זהו מספר מסוג Long, אשר נוצר באופן אוטומטי כאשר נשמר במסד הנתונים. בהתאם לכך, ההערה @Id מציינת לנו בבירור שזהו מזהה ייחודי; @GeneratedValue אחראי לדור הייחודי שלו. ראוי לציין שניתן להוסיף @Id לשדות שאינם נוצרים אוטומטית, אך לאחר מכן יהיה צורך לטפל בסוגיית הייחודיות באופן ידני. מה יכול להיות מזהה עובד ייחודי? ובכן, למשל שם מלא + מחלקה... לעומת זאת, לאדם יש שמות מלאים, ויש סיכוי שהם יעבדו באותה מחלקה, קטן, אבל יש - זה אומר שההחלטה גרועה. אפשר יהיה להוסיף עוד שלל תחומים כמו תאריך העסקה, עיר, אבל כל זה, נראה לי, מסבך את ההיגיון יותר מדי. אתם עשויים לתהות, איך זה בכלל אפשרי שחבורה של תחומים יהיו ייחודיים בבת אחת? אני עונה - אולי. אם אתה סקרן, אתה יכול לחפש בגוגל על ​​דבר כזה כמו @Embeddable ו-@Embedded ובכן, סיימנו עם המהות. עכשיו אנחנו צריכים מאגר פשוט. זה ייראה כך:
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

}
כן, זה הכל. רק ממשק, קראנו לזה EmployeeRepository, הוא מרחיב את JpaRepository שיש לו שני פרמטרים מוקלדים, הראשון אחראי לסוג הנתונים איתו הוא עובד, השני לסוג המפתח. במקרה שלנו, מדובר ב-Employee ו-Long. זה מספיק לעת עתה. המגע האחרון לפני הפעלת האפליקציה יהיה השירות שלנו:
@Service
@RequiredArgsConstructor
public class EmployeeService {

    private final EmployeeRepository employeeRepository;

    public List<Employee> getAllEmployees() {
        return List.of(new Employee().setId(123L));
    }
}
יש את RequiredArgsConstructor המוכר כבר ואת ההערה החדשה @Service - זה מה שמציין בדרך כלל את שכבת ההיגיון העסקי. בעת הפעלת הקשר אביבי, כיתות המסומנות בהערה זו ייווצרו כשעועית. כאשר במחלקת EmployeeController יצרנו את המאפיין הסופי EmployeeService וצירפנו את RequiredArgsConstructor (או יצרנו קונסטרוקטור ביד) Spring, בעת אתחול האפליקציה, הוא ימצא את המקום הזה וישליק לנו אובייקט מחלקה למשתנה זה. ברירת המחדל כאן היא Singleton - כלומר. יהיה אובייקט אחד עבור כל הקישורים הללו; חשוב לקחת זאת בחשבון בעת ​​עיצוב האפליקציה. למעשה, זה הכל, ניתן להפעיל את האפליקציה. אל תשכח להזין את ההגדרות הדרושות בתצורה. REST API או משימת בדיקה אחרת.  - 4 לא אתאר איך להתקין מסד נתונים, ליצור משתמש ואת מסד הנתונים עצמו, אלא רק אציין שבכתובת ה-URL אני משתמש בשני פרמטרים נוספים - useUnicore=true ו-charakterEncoding=UTF-8. זה נעשה כדי שהטקסט יוצג פחות או יותר באופן שווה בכל מערכת. עם זאת, אם אתה עצלן מכדי להתעסק במסד הנתונים ובאמת רוצה לחטט בקוד העבודה, יש פתרון מהיר: 1. הוסף את התלות הבאה ל-build.gradle:
implementation 'com.h2database:h2:2.1.214'
2. ב application.yml אתה צריך לערוך כמה מאפיינים, אני אתן דוגמה מלאה של סעיף האביב לפשטות:
spring:
  application:
    name: "employee-management-service"
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
    database-platform: org.hibernate.dialect.H2Dialect
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:file:./mydb
    username: sa
    password:
מסד הנתונים יאוחסן בתיקיית הפרויקט, בקובץ בשם mydb . אבל הייתי ממליץ להתקין מסד נתונים מלא 😉 מאמר שימושי בנושא: Spring Boot With H2 Database ליתר בטחון, אני אספק גרסה מלאה של ה-build.gradle שלי כדי למנוע אי-התאמות בתלות:
plugins {
	id 'org.springframework.boot' version '2.7.2'
	id 'io.spring.dependency-management' version '1.0.12.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'mysql:mysql-connector-java:8.0.30'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}
המערכת מוכנה להפעלה: REST API או משימת בדיקה אחרת.  - 5 ניתן לבדוק זאת על ידי שליחת בקשת GET מכל תוכנית מתאימה לנקודת הקצה שלנו. במקרה הספציפי הזה, דפדפן רגיל יתאים, אבל בעתיד נצטרך את Postman. REST API או משימת בדיקה אחרת.  - 6 כן, למעשה, עדיין לא יישמנו אף אחת מהדרישות העסקיות, אבל יש לנו כבר אפליקציה שמתחילה וניתנת להרחבה לפונקציונליות הנדרשת. המשך: REST API ואימות נתונים
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION