JavaRush /مدونة جافا /Random-AR /REST API ومهمة اختبار أخرى.
Денис
مستوى
Киев

REST API ومهمة اختبار أخرى.

نشرت في المجموعة
الجزء الأول: البداية من أين نبدأ؟ الغريب بما فيه الكفاية، ولكن من المواصفات الفنية. من المهم للغاية التأكد من أنه بعد قراءة الاختصاصات المقدمة، فإنك تفهم تمامًا ما هو مكتوب فيه وما يتوقعه العميل. أولاً، هذا مهم لمزيد من التنفيذ، وثانياً، إذا لم تقم بتنفيذ ما هو متوقع منك، فلن يكون ذلك في صالحك. لتجنب إهدار الهواء، دعونا نرسم مواصفات فنية بسيطة. لذا، أريد خدمة يمكنني إرسال البيانات إليها، وسيتم تخزينها في الخدمة وإعادتها إليّ متى شئت. أحتاج أيضًا إلى أن أكون قادرًا على تحديث هذه البيانات وحذفها إذا لزم الأمر . بضع جمل لا تبدو وكأنها شيء واضح، أليس كذلك؟ كيف أريد إرسال البيانات هناك؟ ما هي التقنيات المستخدمة؟ ما هو التنسيق الذي ستكون به هذه البيانات؟ لا توجد أيضًا أمثلة للبيانات الواردة والصادرة. الخلاصة - المواصفات الفنية سيئة بالفعل . دعنا نحاول إعادة الصياغة: نحتاج إلى خدمة يمكنها معالجة طلبات HTTP والعمل مع البيانات المنقولة. ستكون هذه قاعدة بيانات سجلات الموظفين. سيكون لدينا موظفين، مقسمين حسب الإدارات والتخصصات، وقد يكون للموظفين مهام تسند إليهم. تتمثل مهمتنا في أتمتة عملية محاسبة الموظفين المعينين والمفصولين والمنقولين، بالإضافة إلى عملية تعيين المهام وإلغائها باستخدام REST API. في المرحلة الأولى، نعمل حاليًا مع الموظفين فقط. يجب أن تحتوي الخدمة على عدة نقاط نهاية للعمل معها: - 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 وsetter. بالنقر فوق الزر "إنشاء"، سنتلقى أرشيفًا بالمشروع الذي يمكن بالفعل تفريغه وفتحه في بيئة التطوير المتكاملة (IDE) المفضلة لدينا. افتراضيًا، سنتلقى مشروعًا فارغًا، مع ملف تكوين لنظام البناء (في حالتي سيكون gradle، ولكن مع Maven لا يوجد فرق جوهري، وملف بدء تشغيل ربيعي واحد) REST API أو مهمة اختبار أخرى.  - 2 يمكن للأشخاص اليقظين الانتباه إلى شيئين . أولاً، لدي ملفي إعدادات application.properties وapplication.yml. افتراضيًا، ستحصل على الخصائص بالضبط - ملف فارغ يمكنك تخزين الإعدادات فيه، ولكن بالنسبة لي يبدو تنسيق yml أكثر قابلية للقراءة، والآن سأعرض مقارنة: REST API أو مهمة اختبار أخرى.  - 3 على الرغم من حقيقة أن الصورة الموجودة على اليسار تبدو أكثر إحكاما فمن السهل رؤية قدر كبير من التكرار في مسار الخصائص. الصورة الموجودة على اليمين عبارة عن ملف yml عادي ذو بنية شجرية يسهل قراءتها. سأستخدم هذا الملف لاحقًا في المشروع. الشيء الثاني الذي قد يلاحظه الأشخاص اليقظون هو أن مشروعي يحتوي بالفعل على عدة حزم. لا توجد تعليمات برمجية معقولة هناك حتى الآن، ولكن الأمر يستحق الاطلاع عليها. كيف يتم كتابة الطلب؟ عند وجود مهمة محددة، يجب علينا تحليلها - تقسيمها إلى مهام فرعية صغيرة والبدء في تنفيذها بشكل متسق. ما هو المطلوب منا؟ نحتاج إلى توفير واجهة برمجة التطبيقات (API) التي يمكن للعميل استخدامها؛ وستكون محتويات حزمة وحدة التحكم مسؤولة عن هذا الجزء من الوظيفة. الجزء الثاني من التطبيق هو قاعدة البيانات - حزمة الثبات. سنقوم فيه بتخزين أشياء مثل كيانات قاعدة البيانات (الكيانات) بالإضافة إلى المستودعات - واجهات زنبركية خاصة تسمح لك بالتفاعل مع قاعدة البيانات. ستحتوي حزمة الخدمة على فئات الخدمة. سنتحدث عن خدمة نوع الربيع أدناه. وأخيرًا وليس آخرًا، حزمة الأدوات. سيتم تخزين الفئات النفعية مع جميع أنواع الأساليب المساعدة هناك، على سبيل المثال، فئات العمل مع التاريخ والوقت، أو فئات العمل مع السلاسل، ومن يعرف ماذا أيضًا. لنبدأ في تنفيذ الجزء الأول من الوظيفة. الجزء الثالث: المراقب المالي
@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، على الرغم من أنه ليس إلزاميًا، إلا أنه يعد تعليقًا توضيحيًا مفيدًا؛ فهو يسمح لك بتعيين مسار محدد لنقطة النهاية. أولئك. من أجل التعامل معها، ستحتاج إلى إرسال الطلبات ليس إلى المضيف المحلي: المنفذ/الموظف، ولكن في هذه الحالة إلى المضيف المحلي:8086/api/v1/employee في الواقع، من أين أتت واجهات برمجة التطبيقات/الإصدار 1 والموظف هذه؟ من 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;
}
ولكن لماذا يجب أن نكتب مثل هذا الشيء إذا كان تعليق توضيحي واحد يكفي؟ :) بالمناسبة، تهانينا، هذا الحقل النهائي الأكثر خصوصية ليس أكثر من حقن التبعية سيئ السمعة. دعنا ننتقل، في الواقع، ما هو نوع المجال الذي يمثله "خدمة الموظف"؟ ستكون هذه إحدى الخدمات في مشروعنا والتي ستقوم بمعالجة الطلبات الخاصة بنقطة النهاية هذه. الفكرة هنا بسيطة جدا. يجب أن يكون لكل فصل مهمته الخاصة ويجب ألا يكون مثقلًا بالإجراءات غير الضرورية. إذا كانت هذه وحدة تحكم، فدعها تعتني بتلقي الطلبات وإرسال الردود، ولكننا نفضل أن نعهد بالمعالجة إلى خدمة إضافية. آخر ما تبقى في هذه الفئة هو الطريقة الوحيدة التي تعرض قائمة بجميع موظفي شركتنا الذين يستخدمون الخدمة المذكورة أعلاه. القائمة نفسها ملفوفة في كيان يسمى ResponseEntity. أفعل ذلك حتى أتمكن في المستقبل، إذا لزم الأمر، من إرجاع رمز الاستجابة والرسالة التي أحتاجها بسهولة، والتي يمكن للنظام الآلي فهمها. لذلك، على سبيل المثال، ستعيد ResponseEntity.ok() الرمز رقم 200، والذي سيقول أن كل شيء على ما يرام، ولكن إذا عدت، على سبيل المثال
return ResponseEntity.badRequest().body(Collections.emptyList());
ثم سيتلقى العميل الرمز 400 - طلب سيء وقائمة فارغة في الاستجابة. عادةً ما يتم إرجاع هذا الرمز إذا كان الطلب غير صحيح. لكن وحدة تحكم واحدة لن تكون كافية بالنسبة لنا لبدء التطبيق. لن تسمح لنا تبعياتنا بالقيام بذلك، لأننا ما زلنا بحاجة إلى قاعدة :) حسنًا، دعنا ننتقل إلى الجزء التالي. الجزء الرابع: المثابرة البسيطة نظرًا لأن مهمتنا الرئيسية هي تشغيل التطبيق، فسوف نقتصر على بضع بذرة في الوقت الحالي. لقد رأيت بالفعل في فئة وحدة التحكم أننا نعيد قائمة كائنات من النوع الموظف، وسيكون هذا هو الكيان الخاص بنا لقاعدة البيانات. لنقم بإنشائه في الحزمة demo.persistence.entity ، وفي المستقبل، يمكن استكمال حزمة الكيان بكيانات أخرى من قاعدة البيانات.
@Entity
@Data
@Accessors(chain = true)
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
}
هذه فئة بسيطة مثل الباب، تقول التعليقات التوضيحية لها بالضبط ما يلي: هذا كيان قاعدة بيانات @Entity، وهذا فئة تحتوي على بيانات @Data - Lombok. سوف يقوم 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")));
}
لا تبدو جميلة جدًا، أليس كذلك؟ نحتاج أيضًا إلى اتباع ترتيب إضافة المتغيرات بدقة وفقًا للمنشئ. ومع ذلك، أنا استطراد، دعونا نعود إلى هذه النقطة. الآن لدينا حقل واحد (إلزامي) فيه - معرف فريد. في هذه الحالة، هذا رقم من النوع الطويل، والذي يتم إنشاؤه تلقائيًا عند حفظه في قاعدة البيانات. وفقًا لذلك، يشير التعليق التوضيحي @Id بوضوح إلى أن هذا معرف فريد، وأن @GeneratedValue هو المسؤول عن إنشائه الفريد. تجدر الإشارة إلى أنه يمكن إضافة @Id إلى الحقول التي لم يتم إنشاؤها تلقائيًا، ولكن يجب التعامل مع مشكلة التفرد يدويًا. ما الذي يمكن أن يكون معرف الموظف الفريد؟ حسنا، على سبيل المثال، الاسم الكامل + القسم... ومع ذلك، فإن الشخص لديه الأسماء الكاملة، وهناك فرصة أن يعملوا في نفس القسم، صغير، ولكن هناك - وهذا يعني أن القرار سيء. سيكون من الممكن إضافة مجموعة من الحقول الأخرى، مثل تاريخ الاستئجار والمدينة، ولكن يبدو لي أن كل هذا يعقد المنطق كثيرًا. قد تتساءل، كيف يمكن لمجموعة من المجالات أن تكون فريدة من نوعها في وقت واحد؟ أجب - ربما. إذا كنت فضوليًا، يمكنك البحث في Google عن شيء مثل @Embeddable و @Embedded حسنًا، لقد انتهينا من الجوهر. الآن نحن بحاجة إلى مستودع بسيط. سوف يبدو مثل هذا:
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

}
أجل هذا كل شئ. مجرد واجهة، أطلقنا عليها اسم "EmployeeRepository"، وهي تمتد إلى JpaRepository الذي يحتوي على معلمتين مكتوبتين، الأول مسؤول عن نوع البيانات الذي يعمل به، والثاني عن نوع المفتاح. في حالتنا، هذه هي الموظف وطويلة. هذا يكفي الآن. اللمسة الأخيرة قبل إطلاق التطبيق ستكون خدمتنا:
@Service
@RequiredArgsConstructor
public class EmployeeService {

    private final EmployeeRepository employeeRepository;

    public List<Employee> getAllEmployees() {
        return List.of(new Employee().setId(123L));
    }
}
يوجد RequiredArgsConstructor المألوف بالفعل والتعليق التوضيحي @Service الجديد - وهذا ما يشير عادةً إلى طبقة منطق الأعمال. عند تشغيل سياق الربيع، سيتم إنشاء الفئات المميزة بهذا التعليق التوضيحي على أنها Beans. عندما نقوم في فئة StaffController بإنشاء الخاصية النهائية Staffervice وإرفاق RequiredArgsConstructor (أو إنشاء مُنشئ يدويًا) Spring، عند تهيئة التطبيق، سيجد هذا المكان ويدخلنا كائن فئة في هذا المتغير. الافتراضي هنا هو Singleton - أي. سيكون هناك كائن واحد لجميع هذه الروابط، وهذا أمر مهم يجب أخذه بعين الاعتبار عند تصميم التطبيق. في الواقع، هذا كل شيء، يمكن تشغيل التطبيق. لا تنس إدخال الإعدادات اللازمة في ملف config. REST API أو مهمة اختبار أخرى.  - 4 لن أصف كيفية تثبيت قاعدة بيانات وإنشاء مستخدم وقاعدة البيانات نفسها، لكنني سأشير فقط إلى أنني أستخدم معلمتين إضافيتين في عنوان URL - useUnicore=true وcharacterEncoding=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