JavaRush /وبلاگ جاوا /Random-FA /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، تنظیمات مصنوع، نسخه جاوا و وابستگی ها را انتخاب کنید. با کلیک بر روی دکمه افزودن وابستگی ها، یک منوی مشخصه با نوار جستجو ظاهر می شود. اولین کاندید برای کلمات rest و data Spring Web و Spring Data هستند - ما آنها را اضافه خواهیم کرد. Lombok یک کتابخانه مناسب است که به شما امکان می دهد از حاشیه نویسی برای خلاص شدن از شر کیلومترها کد با متدهای دریافت کننده و تنظیم کننده استفاده کنید. با کلیک بر روی دکمه Generate، یک بایگانی با پروژه دریافت می کنیم که می تواند از قبل باز شده و در IDE مورد علاقه ما باز شود. به طور پیش فرض، ما یک پروژه خالی با یک فایل پیکربندی برای سیستم ساخت دریافت خواهیم کرد (در مورد من gradle خواهد بود، اما با Maven چیزها تفاوت اساسی ندارند و یک فایل راه اندازی فنری) REST API یا یک کار آزمایشی دیگر.  - 2 افراد توجه می توانند به دو چیز توجه کنند. . اول، من دو فایل تنظیمات application.properties و application.yml دارم. به طور پیش فرض دقیقاً ویژگی ها را دریافت خواهید کرد - یک فایل خالی که می توانید تنظیمات را در آن ذخیره کنید ، اما به نظر من قالب yml کمی خواناتر به نظر می رسد ، اکنون مقایسه ای را نشان می دهم: REST API یا یک کار آزمایشی دیگر.  - 3 علیرغم این واقعیت که تصویر سمت چپ فشرده تر به نظر می رسد. ، به راحتی می توان مقدار زیادی تکرار در مسیر خصوصیات مشاهده کرد. تصویر سمت راست یک فایل yml معمولی با ساختار درختی است که خواندن آن بسیار آسان است. من از این فایل بعدا در پروژه استفاده خواهم کرد. دومین چیزی که افراد توجه ممکن است متوجه شوند این است که پروژه من قبلاً چندین بسته دارد. هنوز هیچ کد منطقی در آنجا وجود ندارد، اما ارزش دارد که آنها را مرور کنید. یک برنامه کاربردی چگونه نوشته می شود؟ با داشتن یک کار خاص، باید آن را تجزیه کنیم - آن را به وظایف فرعی کوچک تقسیم کنیم و اجرای مداوم آنها را شروع کنیم. چه چیزی از ما خواسته می شود؟ ما باید یک API ارائه کنیم که مشتری بتواند از آن استفاده کند؛ محتویات بسته کنترلر مسئول این بخش از عملکرد خواهد بود. بخش دوم برنامه پایگاه داده است - بسته پایداری. در آن ما چیزهایی مانند نهادهای پایگاه داده (موجودات) و همچنین مخازن را ذخیره خواهیم کرد - رابط های فنری ویژه ای که به شما امکان می دهد با پایگاه داده تعامل داشته باشید. بسته خدمات شامل کلاس های خدماتی خواهد بود. در زیر در مورد اینکه سرویس نوع Spring چیست صحبت خواهیم کرد. و در آخر، بسته 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 نیست. بیایید ادامه دهیم، در واقع، خدمات کارکنان چه نوع زمینه ای است؟ این یکی از خدمات پروژه ما خواهد بود که درخواست‌های این نقطه پایانی را پردازش می‌کند. ایده در اینجا بسیار ساده است. هر کلاس باید وظایف خاص خود را داشته باشد و نباید با اقدامات غیر ضروری بارگذاری شود. اگر این یک کنترل‌کننده است، اجازه دهید مراقب دریافت درخواست‌ها و ارسال پاسخ باشد، اما ما ترجیح می‌دهیم پردازش را به یک سرویس اضافی بسپاریم. آخرین چیزی که در این کلاس باقی می ماند تنها روشی است که لیستی از تمام کارکنان شرکت ما را با استفاده از سرویس فوق الذکر برمی گرداند. خود لیست در موجودی به نام 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;
}
این یک کلاس به سادگی یک در است که Annotations آن دقیقاً موارد زیر را می گوید: این یک موجودیت پایگاه داده @Entity است، این یک کلاس با داده های @Data - Lombok است. Lombok مفید همه گیرها، تنظیم کننده ها، سازنده های لازم را برای ما ایجاد می کند - پر کردن کامل. خوب، کمی گیلاس روی کیک @Accessors است (زنجیره = درست) در واقع، این یک پیاده سازی پنهان از الگوی بیلدر است. فرض کنید شما یک کلاس با دسته ای از فیلدها دارید که می خواهید آنها را نه از طریق سازنده، بلکه با روش ها اختصاص دهید. به ترتیب متفاوت، شاید نه همه در یک زمان. شما هرگز نمی دانید که چه نوع منطقی در برنامه شما خواهد بود. این حاشیه نویسی کلید شما برای این کار است. بیایید نگاه بیندازیم:
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 را گسترش می دهد که دو پارامتر تایپ شده دارد، اولی مسئول نوع داده ای است که با آن کار می کند، دومی برای نوع کلید. در مورد ما، اینها کارمند و طولانی هستند. فعلا همین کافی است. آخرین تماس قبل از راه اندازی برنامه، خدمات ما خواهد بود:
@Service
@RequiredArgsConstructor
public class EmployeeService {

    private final EmployeeRepository employeeRepository;

    public List<Employee> getAllEmployees() {
        return List.of(new Employee().setId(123L));
    }
}
RequiredArgsConstructor از قبل آشنا و حاشیه نویسی @Service جدید وجود دارد - این همان چیزی است که معمولاً لایه منطق تجاری را نشان می دهد. هنگام اجرای یک زمینه بهار، کلاس‌هایی که با این حاشیه‌نویسی مشخص شده‌اند به‌عنوان Beans ایجاد می‌شوند. هنگامی که در کلاس EmployeeController، ویژگی نهایی EmployeeService را ایجاد کرده و RequiredArgsConstructor را (یا سازنده با دست) Spring را ضمیمه کردیم، هنگام مقداردهی اولیه برنامه، این مکان را پیدا می کند و یک شی کلاس را در این متغیر برای ما قرار می دهد. پیش فرض در اینجا Singleton است - i.e. برای همه این پیوندها یک شی وجود دارد؛ این مهم است که هنگام طراحی برنامه در نظر گرفته شود. در واقع، این همه چیز است، برنامه را می توان راه اندازی کرد. فراموش نکنید که تنظیمات لازم را در تنظیمات وارد کنید. REST API یا یک کار آزمایشی دیگر.  - 4 من نحوه نصب یک پایگاه داده، ایجاد یک کاربر و خود پایگاه داده را توضیح نمی دهم، اما فقط توجه می کنم که در URL از دو پارامتر اضافی استفاده می کنم - useUnicore=true و characterEncoding=UTF-8. این کار به این دلیل انجام شد که متن در هر سیستمی کم و بیش یکسان نمایش داده شود. با این حال، اگر خیلی تنبل هستید که با دیتابیس سر و کله بزنید و واقعاً می خواهید کدهای کار را بررسی کنید، یک راه حل سریع وجود دارد: 1. وابستگی زیر را به build.gradle اضافه کنید:
implementation 'com.h2database:h2:2.1.214'
2. در application.yml شما باید چندین ویژگی را ویرایش کنید، من برای سادگی یک مثال کامل از قسمت Spring را می زنم:
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 با پایگاه داده H2 در هر صورت، من یک نسخه کامل از 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 و Data Validation
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION