JavaRush /Java Blogu /Random-AZ /REST API və başqa bir test tapşırığı.
Денис
Səviyyə
Киев

REST API və başqa bir test tapşırığı.

Qrupda dərc edilmişdir
I hissə: Başlanğıc Hardan başlamaq lazımdır? Qəribədir, amma texniki xüsusiyyətlərdən. Təqdim edilmiş TOR-u oxuduqdan sonra orada nə yazıldığını və müştərinin nə gözlədiyini tam başa düşdüyünüzə əmin olmaq son dərəcə vacibdir. Birincisi, bu, sonrakı həyata keçirilməsi üçün vacibdir, ikincisi, sizdən gözlənilənləri həyata keçirməsəniz, bu sizin xeyrinizə olmayacaq. Hava itkisinin qarşısını almaq üçün sadə bir texniki spesifikasiyanı təsvir edək. Beləliklə, mən məlumat göndərə biləcəyim bir xidmət istəyirəm, o, xidmətdə saxlanacaq və istədiyim kimi mənə qaytarılacaq. Lazım gələrsə, mən də bu məlumatları yeniləməyi və silməyi bacarmalıyam . Bir-iki cümlə aydın bir şey kimi görünmür, elə deyilmi? Məlumatı ora necə göndərmək istəyirəm? Hansı texnologiyalardan istifadə etməli? Bu məlumat hansı formatda olacaq? Gələn və gedən məlumatların nümunələri də yoxdur. Nəticə - texniki göstəricilər artıq pisdir . Yenidən ifadə etməyə çalışaq: Bizə HTTP sorğularını emal edə bilən və ötürülən məlumatlarla işləyə bilən xidmət lazımdır. Bu kadr qeydləri bazası olacaq. İşçilərimiz olacaq, onlar şöbələrə və ixtisaslara görə bölünür, işçilərin onlara tapşırılan vəzifələri ola bilər. Bizim vəzifəmiz işə götürülən, işdən çıxarılan, köçürülən işçilərin uçotu prosesini, həmçinin REST API-dən istifadə edərək tapşırıqların təyin edilməsi və ləğv edilməsi prosesini avtomatlaşdırmaqdır. Faza 1 olaraq biz hazırda yalnız işçilərlə işləyirik. Xidmətin onunla işləmək üçün bir neçə son nöqtəsi olmalıdır: - POST /işçi - POST sorğusu, işçi haqqında məlumatı olan JSON obyektini qəbul etməlidir. Bu obyekt verilənlər bazasında saxlanmalıdır, əgər belə bir obyekt verilənlər bazasında artıq mövcuddursa, sahələrdəki məlumatlar yeni məlumatlar ilə yenilənməlidir. - GET /işçi - Verilənlər bazasında saxlanmış işçilərin bütün siyahısını qaytaran GET sorğusu - SİLİN - SİLİN / işçinin konkret işçini silmək üçün İşçi məlumat modeli:
{
  "firstName": String,
  "lastName": String,
  "department": String,
  "salary": String
  "hired": String //"yyyy-mm-dd"
  "tasks": [
  	//List of tasks, not needed for Phase 1
  ]
}
II hissə: İş üçün alətlər Beləliklə, işin əhatə dairəsi az-çox aydındır, amma biz bunu necə edəcəyik? Aydındır ki, testdəki bu cür tapşırıqlar bir neçə tətbiq məqsədi ilə verilir, necə kodladığınızı görmək, sizi Spring istifadə etməyə və verilənlər bazası ilə bir az işləməyə məcbur etməkdir. Yaxşı, bunu edək. Bizə REST API dəstəyi və verilənlər bazası ilə SpringBoot layihəsi lazımdır. https://start.spring.io/ saytında sizə lazım olan hər şeyi tapa bilərsiniz. REST API və ya başqa test tapşırığı.  - 1 Quraşdırma sistemini, dili, SpringBoot versiyasını seçə, artefakt parametrlərini, Java versiyasını və asılılıqları təyin edə bilərsiniz. Asılılıqları əlavə et düyməsini kliklədikdə, axtarış çubuğu ilə xarakterik bir menyu görünəcəkdir. İstirahət və məlumat sözləri üçün ilk namizədlər Spring Web və Spring Data-dır - biz onları əlavə edəcəyik. Lombok, alıcı və təyinedici üsullarla kilometrlərlə koddan xilas olmaq üçün annotasiyalardan istifadə etməyə imkan verən rahat kitabxanadır. Yarat düyməsini klikləməklə, biz artıq açıla və sevimli IDE-də açıla bilən layihə ilə arxiv alacağıq. Varsayılan olaraq, quruluş sistemi üçün konfiqurasiya faylı olan boş bir layihə alacağıq (mənim vəziyyətimdə bu gradle olacaq, lakin Maven ilə heç bir əsas fərq yoxdur və bir bahar başlanğıc faylı) REST API və ya başqa test tapşırığı.  - 2 Diqqətli insanlar iki şeyə diqqət yetirə bilər. . Birincisi, mənim application.properties və application.yml iki parametr faylım var. Varsayılan olaraq, tam olaraq xassələri əldə edəcəksiniz - parametrləri saxlaya biləcəyiniz boş bir fayl, amma mənə yml formatı bir az daha oxunaqlı görünür, indi bir müqayisə göstərəcəyəm: REST API və ya başqa test tapşırığı.  - 3 Soldakı şəkil daha yığcam görünməsinə baxmayaraq , xassələr yolunda çoxlu sayda təkrarlama görmək asandır. Sağdakı şəkil, oxunması olduqca asan olan ağac quruluşuna malik adi yml faylıdır. Bu faylı daha sonra layihədə istifadə edəcəyəm. Diqqətli insanların fərq edə biləcəyi ikinci şey layihəmdə artıq bir neçə paketin olmasıdır. Orada hələ sağlam kod yoxdur, amma onlardan keçməyə dəyər. Ərizə necə yazılır? Müəyyən bir vəzifəmiz olduqda, onu parçalamalıyıq - onu kiçik alt tapşırıqlara ayırmalı və ardıcıl icrasına başlamalıyıq. Bizdən nə tələb olunur? Müştərinin istifadə edə biləcəyi API təmin etməliyik; nəzarətçi paketinin məzmunu funksionallığın bu hissəsinə cavabdeh olacaq. Tətbiqin ikinci hissəsi verilənlər bazasıdır - əzmkarlıq paketidir. Orada verilənlər bazası obyektləri (müəssisələr), eləcə də Repozitoriyalar kimi şeyləri saxlayacağıq - verilənlər bazası ilə qarşılıqlı əlaqə yaratmağa imkan verən xüsusi yay interfeysləri. Xidmət paketində xidmət sinifləri olacaq. Bahar tipli Xidmətin nə olduğu haqqında aşağıda danışacağıq. Və nəhayət, utils paketi. Orada hər cür yardımçı metodları olan utilitar siniflər saxlanılacaq, məsələn, tarix və vaxtla işləmək üçün dərslər və ya simlərlə işləmək üçün dərslər və kim bilir daha nələr var. Funksionallığın birinci hissəsini həyata keçirməyə başlayaq. III hissə: Nəzarətçi
@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());
    }
}
İndi EmployeeController sinifimiz belə görünür. Burada diqqət yetirməyə dəyər bir sıra vacib məqamlar var. 1. Sinif üzərindəki qeydlər, birinci @RestController tətbiqimizə bu sinfin son nöqtə olacağını bildirir. 2. @RequestMapping, məcburi olmasa da, faydalı annotasiyadır, son nöqtə üçün xüsusi yol təyin etməyə imkan verir. Bunlar. onu döymək üçün sorğuları localhost:port/employee-ə deyil, bu halda localhost:8086/api/v1/employee-ə göndərməli olacaqsınız Əslində, bu api/v1 və işçi haradan gəldi? Application.yml saytından diqqətlə baxsanız, orada aşağıdakı sətirləri tapa bilərsiniz:
application:
  endpoint:
    root: api/v1
    employee: employee
    task: task
Gördüyünüz kimi, application.endpoint.root və application.endpoint.employee kimi dəyişənlərimiz var, bunlar annotasiyalarda yazdıqlarımdır, mən bu metodu xatırlamağı məsləhət görürəm - bu, faylı genişləndirməyə və ya yenidən yazmağa çox vaxt qənaət edəcək. funksionallıq - konfiqurasiyada hər şeyin olması həmişə daha rahatdır və bütün layihəni kodlaşdırmamaq lazımdır. 3. @RequiredArgsConstructor Lombok annotasiyasıdır, lazımsız şeyləri yazmaqdan qaçmağa imkan verən rahat kitabxanadır. Bu halda, annotasiya sinfin bütün sahələri yekun olaraq qeyd olunan ictimai konstruktora malik olacağına bərabərdir.
public EmployeeController(EmployeeService employeeService) {
    this.employeeService=employeeService;
}
Bəs bir annotasiya kifayətdirsə, niyə belə bir şey yazmalıyıq? :) Yeri gəlmişkən, təbrik edirəm, bu ən özəl final sahəsi bədnam Dependency Injection-dan başqa bir şey deyil. Gəlin davam edək, əslində, staffeservice hansı sahədir? Bu, layihəmizdə bu son nöqtə üçün sorğuları emal edəcək xidmətlərdən biri olacaq. Burada fikir çox sadədir. Hər bir sinfin öz vəzifəsi olmalıdır və lazımsız hərəkətlərlə yüklənməməlidir. Bu nəzarətçidirsə, sorğuların qəbulu və cavabların göndərilməsi ilə məşğul olsun, lakin biz emalını əlavə xidmətə həvalə etmək istərdik. Bu sinifdə qalan son şey, yuxarıda göstərilən xidmətdən istifadə edən şirkətimizin bütün işçilərinin siyahısını qaytaran yeganə üsuldur. Siyahının özü ResponseEntity adlı bir quruma bükülür. Bunu ona görə edirəm ki, gələcəkdə lazım gələrsə, avtomatlaşdırılmış sistemin başa düşə biləcəyi cavab kodunu və mənə lazım olan mesajı asanlıqla qaytara biləcəm. Beləliklə, məsələn, ResponseEntity.ok() hər şeyin yaxşı olduğunu deyəcək 200-cü kodu qaytaracaq, amma məsələn, qayıtsam
return ResponseEntity.badRequest().body(Collections.emptyList());
sonra müştəri kodu 400 - bad reuqest və cavabda boş siyahı alacaq. Adətən bu kod sorğu səhv olarsa qaytarılır. Ancaq tətbiqi işə salmaq üçün bir nəzarətçi kifayət etməyəcək. Asılılıqlarımız bunu etməyə imkan verməyəcək, çünki hələ də bazamız olmalıdır :) Yaxşı, keçək növbəti hissəyə. IV hissə: sadə əzmkarlıq Bizim əsas vəzifəmiz tətbiqi işə salmaq olduğundan, biz hələlik özümüzü bir neçə qaralama ilə məhdudlaşdıracağıq. Siz artıq Controller sinfində gördünüz ki, biz Employee tipli obyektlərin siyahısını qaytarırıq, bu, verilənlər bazası üçün bizim obyektimiz olacaq. Gəlin onu demo.persistence.entity paketində yaradaq.Gələcəkdə obyekt paketi verilənlər bazasından digər obyektlərlə əlavə oluna bilər.
@Entity
@Data
@Accessors(chain = true)
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
}
Bu, qapı kimi sadə bir sinifdir, Annotasiyaları dəqiq olaraq aşağıdakıları deyir: bu @Entity verilənlər bazası varlığıdır, bu @Data - Lombok verilənləri olan bir sinifdir. Faydalı Lombok bizim üçün bütün lazımi alıcıları, tənzimləyiciləri, konstruktorları yaradacaq - tam doldurma. Yaxşı, tortda bir az albalı @Accessors (zəncir = doğrudur) Əslində, bu Builder modelinin gizli tətbiqidir. Tutaq ki, konstruktor vasitəsilə deyil, metodlarla təyin etmək istədiyiniz bir dəstə sahəyə malik bir sinifiniz var. Fərqli ardıcıllıqla, bəlkə də hamısı eyni anda deyil. Tətbiqinizdə hansı məntiqin olacağını heç vaxt bilmirsiniz. Bu annotasiya bu tapşırığın açarıdır. Baxaq:
public Employee createEmployee() {
    return new Employee().setName("Peter")
        				.setAge("28")
        				.setDepartment("IT");
}
Tutaq ki, sinifimizdə bütün bu sahələr var😄Onları təyin edə bilərsiniz, təyin edə bilməzsiniz, onları yerlərdə qarışdıra bilərsiniz. Cəmi 3 mülkə gəldikdə, bu, əla bir şey kimi görünmür. Amma xassələri daha çox sayda siniflər var, məsələn 50. Və kimi bir şey yazın
public Employee createEmployee() {
    return new Employee("Peter", "28", "IT", "single", "loyal", List.of(new Task("do Something 1"), new Task ("do Something 2")));
}
Çox gözəl görünmür, elə deyilmi? Konstruktora uyğun olaraq dəyişənlərin əlavə edilməsi qaydasına da ciddi əməl etməliyik. Bununla belə, mən kənara çəkilirəm, gəlin mətləbə qayıdaq. İndi bizdə bir (məcburi) sahə var - unikal identifikator. Bu halda, bu verilənlər bazasında saxlandıqda avtomatik olaraq yaradılan Uzun tipli nömrədir. Müvafiq olaraq, @Id annotasiyası bizə bunun unikal identifikator olduğunu açıq şəkildə göstərir; @GeneratedValue onun unikal generasiyası üçün cavabdehdir. Qeyd etmək lazımdır ki, @Id avtomatik yaradılmayan sahələrə əlavə edilə bilər, lakin sonra unikallıq məsələsi əl ilə həll edilməlidir. Unikal işçi identifikatoru nə ola bilər? Yaxşı, məsələn, tam ad + şöbə... bununla belə, insanın tam adları var və onların eyni şöbədə işləmək şansı var, kiçik, amma var - bu, qərarın pis olduğunu göstərir. İşə alınma tarixi, şəhər kimi bir dəstə başqa sahələri də əlavə etmək olardı, amma bütün bunlar, mənə elə gəlir ki, məntiqi həddən artıq çətinləşdirir. Düşünə bilərsiniz ki, bir dəstə sahənin bir anda unikal olması necə mümkündür? Cavab verirəm - bəlkə. Əgər maraqlanırsınızsa, @Embeddable və @Embedded kimi bir şey haqqında google-da axtara bilərsiniz. Yaxşı, mahiyyəti bitirdik. İndi bizə sadə bir depo lazımdır. Bu belə görünəcək:
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

}
Bəli, hamısı budur. Sadəcə bir interfeys, biz onu EmployeeRepository adlandırdıq, o, iki tipli parametrləri olan JpaRepository-ni genişləndirir, birincisi işlədiyi məlumat növü üçün, ikincisi isə açar növü üçün cavabdehdir. Bizim vəziyyətimizdə bunlar İşçi və Uzundur. Hələlik bu kifayətdir. Tətbiqi işə salmazdan əvvəl son toxunuş bizim xidmətimiz olacaq:
@Service
@RequiredArgsConstructor
public class EmployeeService {

    private final EmployeeRepository employeeRepository;

    public List<Employee> getAllEmployees() {
        return List.of(new Employee().setId(123L));
    }
}
Artıq tanış olan RequiredArgsConstructor və yeni @Service annotasiyası var - bu adətən biznes məntiqi qatını ifadə edir. Yaz kontekstini işlədərkən, bu annotasiya ilə qeyd olunan siniflər Fasulye kimi yaradılacaq. EmployeeController sinfində biz son EmployeeService xassəsini yaratdıqda və RequiredArgsConstructor əlavə etdikdə (və ya əl ilə konstruktor yaratdıq) Bahar, tətbiqi işə saldıqda, o, bu yeri tapacaq və bu dəyişənə bizə bir sinif obyekti keçirəcək. Burada standart Singleton - yəni. bütün bu cür bağlantılar üçün bir obyekt olacaq; tətbiqi tərtib edərkən bunu nəzərə almaq vacibdir. Əslində, hamısı budur, tətbiq işə salına bilər. Konfiqurasiyada lazımi parametrləri daxil etməyi unutmayın. REST API və ya başqa test tapşırığı.  - 4 Verilənlər bazasını necə quraşdırmağı, istifadəçini və verilənlər bazasını necə yaratmağı təsvir etməyəcəyəm, ancaq qeyd edəcəm ki, URL-də mən iki əlavə parametrdən istifadə edirəm - useUnicore=true və characterEncoding=UTF-8. Bu, mətnin hər hansı bir sistemdə az və ya çox bərabər şəkildə göstərilməsi üçün edilib. Bununla belə, əgər siz verilənlər bazası ilə işləmək üçün çox tənbəlsinizsə və həqiqətən də iş kodunu araşdırmaq istəyirsinizsə, sürətli həll yolu var: 1. build.gradle-ə aşağıdakı asılılığı əlavə edin:
implementation 'com.h2database:h2:2.1.214'
2. Application.yml-də bir neçə xüsusiyyəti redaktə etməlisiniz, sadəlik üçün yaz bölməsinin tam nümunəsini verəcəyəm:
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:
Verilənlər bazası layihə qovluğunda mydb adlı faylda saxlanılacaq . Ancaq tam hüquqlu verilənlər bazası quraşdırmağı tövsiyə edərdim 😉 Mövzu ilə bağlı faydalı məqalə: H2 verilənlər bazası ilə Spring Boot Hər halda, asılılıqlardakı uyğunsuzluqları aradan qaldırmaq üçün build.gradle-in tam versiyasını təqdim edəcəyəm:
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()
}
Sistem işə salınmağa hazırdır: REST API və ya başqa test tapşırığı.  - 5 İstənilən uyğun proqramdan son nöqtəmizə GET sorğusu göndərməklə onu yoxlaya bilərsiniz. Bu vəziyyətdə, adi bir brauzer işləyəcək, lakin gələcəkdə bizə Postman lazımdır. REST API və ya başqa test tapşırığı.  - 6 Bəli, əslində, biz hələ heç bir biznes tələbini həyata keçirməmişik, lakin bizdə artıq başlayan və tələb olunan funksionallıq üçün genişləndirilə bilən bir proqram var. Davamı: REST API və Data Validation
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION