JavaRush /Java Blog /Random-TL /REST API at isa pang pagsubok na gawain.
Денис
Antas
Киев

REST API at isa pang pagsubok na gawain.

Nai-publish sa grupo
Bahagi I: Simula Saan magsisimula? Kakatwa, ngunit mula sa mga teknikal na pagtutukoy. Napakahalaga na siguraduhin na pagkatapos basahin ang isinumiteng TOR ay lubos mong nauunawaan kung ano ang nakasulat dito at kung ano ang inaasahan ng kliyente. Una, ito ay mahalaga para sa karagdagang pagpapatupad, at pangalawa, kung hindi mo ipapatupad ang inaasahan sa iyo, hindi ito magiging kalamangan mo. Upang maiwasan ang pag-aaksaya ng hangin, mag-sketch tayo ng isang simpleng teknikal na detalye. Kaya, gusto ko ng isang serbisyo kung saan maaari akong magpadala ng data, ito ay maiimbak sa serbisyo at ibabalik sa akin sa kalooban. Kailangan ko ring ma-update at tanggalin ang data na ito kung kinakailangan . Ang isang pares ng mga pangungusap ay tila hindi isang malinaw na bagay, tama? Paano ko gustong magpadala ng data doon? Anong mga teknolohiya ang gagamitin? Anong format ang magiging data na ito? Wala ring mga halimbawa ng papasok at papalabas na data. Konklusyon - ang teknikal na detalye ay masama na . Subukan nating i-rephrase: Kailangan natin ng serbisyo na maaaring magproseso ng mga kahilingan sa HTTP at gumana sa inilipat na data. Ito ang magiging database ng mga rekord ng tauhan. Magkakaroon tayo ng mga empleyado, sila ay nahahati sa mga departamento at mga espesyalidad, ang mga empleyado ay maaaring may mga gawain na itinalaga sa kanila. Ang aming gawain ay i-automate ang proseso ng accounting para sa mga natanggap, tinanggal, inilipat na mga empleyado, pati na rin ang proseso ng pagtatalaga at pagkansela ng mga gawain gamit ang REST API. Bilang Phase 1, kami ay kasalukuyang nagtatrabaho lamang sa mga empleyado. Ang serbisyo ay dapat na may ilang mga endpoint upang gumana dito: - POST /empleyado - POST kahilingan, na dapat tumanggap ng JSON object na may data tungkol sa empleyado. Ang bagay na ito ay dapat na i-save sa database; kung ang naturang bagay ay mayroon na sa database, ang impormasyon sa mga patlang ay dapat na ma-update gamit ang bagong data. - GET /employee - GET request na nagbabalik ng buong listahan ng mga empleyadong naka-save sa database - DELETE - DELETE /employee para tanggalin ang isang partikular na modelo ng data ng empleyado ng empleyado:
{
  "firstName": String,
  "lastName": String,
  "department": String,
  "salary": String
  "hired": String //"yyyy-mm-dd"
  "tasks": [
  	//List of tasks, not needed for Phase 1
  ]
}
Bahagi II: Mga tool para sa trabaho Kaya, ang saklaw ng trabaho ay higit pa o hindi gaanong malinaw, ngunit paano natin ito gagawin? Malinaw, ang mga naturang gawain sa pagsusulit ay ibinibigay kasama ng ilang layunin sa aplikasyon, upang makita kung paano ka mag-code, upang pilitin kang gumamit ng Spring at magtrabaho nang kaunti sa database. Well, gawin natin ito. Kailangan namin ng proyekto ng SpringBoot na may suporta sa REST API at isang database. Sa website na https://start.spring.io/ mahahanap mo ang lahat ng kailangan mo. REST API o isa pang pagsubok na gawain.  - 1 Maaari mong piliin ang build system, wika, bersyon ng SpringBoot, itakda ang mga setting ng artifact, bersyon ng Java, at mga dependency. Ang pag-click sa button na Magdagdag ng Dependencies ay maglalabas ng isang katangian na menu na may isang search bar. Ang mga unang kandidato para sa mga salitang rest at data ay Spring Web at Spring Data - idaragdag namin ang mga ito. Ang Lombok ay isang maginhawang library na nagbibigay-daan sa iyong gumamit ng mga anotasyon upang maalis ang mga kilometro ng code gamit ang mga pamamaraan ng getter at setter. Sa pamamagitan ng pag-click sa pindutang Bumuo ay makakatanggap kami ng isang archive na may proyekto na maaari nang i-unpack at mabuksan sa aming paboritong IDE. Bilang default, makakatanggap kami ng isang walang laman na proyekto, na may isang configuration file para sa build system (sa aking kaso ito ay magiging gradle, ngunit sa Maven bagay ay walang pangunahing pagkakaiba, at isang spring startup file) Ang mga taong matulungin ay maaaring magbayad ng pansin sa dalawang REST API o isa pang pagsubok na gawain.  - 2 bagay . Una, mayroon akong dalawang file ng mga setting na application.properties at application.yml. Bilang default, makakakuha ka ng eksaktong mga katangian - isang walang laman na file kung saan maaari kang mag-imbak ng mga setting, ngunit para sa akin ang format ng yml ay mukhang mas madaling mabasa, ngayon ay magpapakita ako ng paghahambing: Sa kabila ng katotohanan na ang larawan REST API o isa pang pagsubok na gawain.  - 3 sa kaliwa ay mukhang mas compact. , madaling makakita ng malaking halaga ng pagdoble sa path ng mga katangian. Ang larawan sa kanan ay isang regular na yml file na may istraktura ng puno na medyo madaling basahin. Gagamitin ko ang file na ito mamaya sa proyekto. Ang pangalawang bagay na maaaring mapansin ng mga taong matulungin ay ang aking proyekto ay mayroon nang ilang mga pakete. Wala pang tamang code doon, ngunit sulit na dumaan sa mga ito. Paano isinusulat ang isang aplikasyon? Sa pagkakaroon ng isang partikular na gawain, dapat nating i-decompose ito - hatiin ito sa maliliit na subtask at simulan ang kanilang pare-parehong pagpapatupad. Ano ang hinihiling sa atin? Kailangan naming magbigay ng API na magagamit ng kliyente; ang mga nilalaman ng controller package ay magiging responsable para sa bahaging ito ng functionality. Ang pangalawang bahagi ng application ay ang database - ang pakete ng pagtitiyaga. Sa loob nito ay mag-iimbak kami ng mga bagay tulad ng Mga Entidad ng Database (Entity) pati na rin ang Mga Repositori - mga espesyal na interface ng tagsibol na nagbibigay-daan sa iyong makipag-ugnayan sa database. Ang package ng serbisyo ay maglalaman ng mga klase ng serbisyo. Pag-uusapan natin kung ano ang Spring type Service sa ibaba. At huling ngunit hindi bababa sa, ang utils package. Ang mga utilitarian na klase na may lahat ng uri ng mga pantulong na pamamaraan ay iimbak doon, halimbawa, mga klase para sa pagtatrabaho sa petsa at oras, o mga klase para sa pagtatrabaho sa mga string, at kung sino pa ang nakakaalam. Simulan natin ang pagpapatupad ng unang bahagi ng pag-andar. Bahagi III: Kontroler
@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());
    }
}
Ngayon ang aming EmployeeController class ay ganito ang hitsura. Mayroong ilang mahahalagang bagay na dapat bigyang pansin dito. 1. Mga anotasyon sa itaas ng klase, ang unang @RestController ay nagsasabi sa aming application na ang klase na ito ay magiging isang endpoint. 2. Ang @RequestMapping, bagama't hindi sapilitan, ay isang kapaki-pakinabang na anotasyon, nagbibigay-daan ito sa iyong magtakda ng partikular na landas para sa endpoint. Yung. upang kumatok dito, kakailanganin mong magpadala ng mga kahilingan hindi sa localhost:port/employee, ngunit sa kasong ito sa localhost:8086/api/v1/employee Sa totoo lang, saan nagmula ang mga api/v1 at empleyadong ito? Mula sa aming application.yml Kung titingnan mong mabuti, makikita mo ang mga sumusunod na linya doon:
application:
  endpoint:
    root: api/v1
    employee: employee
    task: task
Tulad ng nakikita mo, mayroon kaming mga variable tulad ng application.endpoint.root at application.endpoint.employee, ito mismo ang isinulat ko sa mga anotasyon, inirerekumenda kong tandaan ang pamamaraang ito - makakatipid ito ng maraming oras sa pagpapalawak o muling pagsulat ng functionality - ito ay palaging mas maginhawa upang magkaroon ng lahat sa config , at hindi hardcode ang buong proyekto. 3. Ang @RequiredArgsConstructor ay isang Lombok annotation, isang maginhawang library na nagbibigay-daan sa iyong maiwasan ang pagsusulat ng mga hindi kinakailangang bagay. Sa kasong ito, ang anotasyon ay katumbas ng katotohanan na ang klase ay magkakaroon ng pampublikong tagapagbuo na ang lahat ng mga patlang ay minarkahan bilang pangwakas.
public EmployeeController(EmployeeService employeeService) {
    this.employeeService=employeeService;
}
Ngunit bakit natin isusulat ang ganoong bagay kung sapat na ang isang anotasyon? :) By the way, congratulations, itong pinakapribado na final field ay walang iba kundi ang kilalang Dependency Injection. Let's move on, actually, anong klaseng field ang employeeService? Isa ito sa mga serbisyo sa aming proyekto na magpoproseso ng mga kahilingan para sa endpoint na ito. Ang ideya dito ay napaka-simple. Ang bawat klase ay dapat magkaroon ng sarili nitong gawain at hindi dapat ma-overload ng mga hindi kinakailangang aksyon. Kung isa itong controller, hayaan itong mag-asikaso sa pagtanggap ng mga kahilingan at pagpapadala ng mga tugon, ngunit mas gusto naming ipagkatiwala ang pagproseso sa isang karagdagang serbisyo. Ang huling bagay na natitira sa klase na ito ay ang tanging paraan na nagbabalik ng listahan ng lahat ng empleyado ng aming kumpanya gamit ang nabanggit na serbisyo. Ang listahan mismo ay nakabalot sa isang entity na tinatawag na ResponseEntity. Ginagawa ko ito upang sa hinaharap, kung kinakailangan, madali kong maibalik ang code ng tugon at mensahe na kailangan ko, na mauunawaan ng automated system. Kaya, halimbawa, ibabalik ng ResponseEntity.ok() ang ika-200 na code, na magsasabing maayos ang lahat, ngunit kung babalik ako, halimbawa
return ResponseEntity.badRequest().body(Collections.emptyList());
pagkatapos ay makakatanggap ang kliyente ng code 400 - masamang reuqest at isang walang laman na listahan sa tugon. Karaniwang ibinabalik ang code na ito kung mali ang kahilingan. Ngunit hindi sapat ang isang controller para simulan natin ang application. Hindi tayo papayagan ng ating mga dependency na gawin ito, dahil dapat may base pa rin tayo :) Well, let's move on to the next part. Bahagi IV: simpleng pagtitiyaga Dahil ang aming pangunahing gawain ay ilunsad ang application, lilimitahan namin ang aming sarili sa ilang mga stub sa ngayon. Nakita mo na sa klase ng Controller na nagbabalik kami ng isang listahan ng mga bagay na may uri ng Empleyado, ito ang magiging entity namin para sa database. Gawin natin ito sa demo.persistence.entity package . Sa hinaharap, ang entity package ay maaaring dagdagan ng iba pang entity mula sa database.
@Entity
@Data
@Accessors(chain = true)
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
}
Ito ay isang klase na kasing simple ng isang pinto, ang Anotasyon nito ay eksaktong nagsasabi ng sumusunod: ito ay isang database entity @Entity, ito ay isang klase na may data @Data - Lombok. Ang kapaki-pakinabang na Lombok ay lilikha para sa amin ng lahat ng kinakailangang getter, setter, constructor - kumpletong pagpupuno. Well, ang isang maliit na cherry sa cake ay @Accessors(chain = true) Sa katunayan, ito ay isang nakatagong pagpapatupad ng Builder pattern. Ipagpalagay na mayroon kang isang klase na may isang bungkos ng mga patlang na nais mong italaga hindi sa pamamagitan ng constructor, ngunit sa pamamagitan ng mga pamamaraan. Sa magkaibang pagkakasunud-sunod, marahil hindi lahat sa parehong oras. Hindi mo alam kung anong uri ng lohika ang magiging sa iyong aplikasyon. Ang anotasyong ito ay ang iyong susi sa gawaing ito. Tingnan natin:
public Employee createEmployee() {
    return new Employee().setName("Peter")
        				.setAge("28")
        				.setDepartment("IT");
}
Ipagpalagay natin na nasa klase natin ang lahat ng field na ito😄Pwede mong i-assign, hindi mo i-assign, pwede mong ihalo sa mga lugar. Sa kaso ng 3 pag-aari lamang, ito ay hindi mukhang isang namumukod-tanging bagay. Ngunit may mga klase na may mas malaking bilang ng mga katangian, halimbawa 50. At magsulat ng isang bagay tulad ng
public Employee createEmployee() {
    return new Employee("Peter", "28", "IT", "single", "loyal", List.of(new Task("do Something 1"), new Task ("do Something 2")));
}
Mukhang hindi masyadong maganda, hindi ba? Kailangan din nating mahigpit na sundin ang pagkakasunud-sunod ng pagdaragdag ng mga variable alinsunod sa constructor. Gayunpaman, lumihis ako, bumalik tayo sa punto. Ngayon ay mayroon na tayong isang (mandatory) na field dito - isang natatanging identifier. Sa kasong ito, ito ay isang Mahabang uri ng numero, na awtomatikong nabuo kapag nai-save sa database. Alinsunod dito, ang @Id annotation ay malinaw na nagpapahiwatig sa amin na ito ay isang natatanging identifier; @GeneratedValue ang responsable para sa natatanging henerasyon nito. Ito ay nagkakahalaga ng pagpuna na ang @Id ay maaaring idagdag sa mga patlang na hindi awtomatikong nabuo, ngunit pagkatapos ay ang isyu ng pagiging natatangi ay kailangang harapin nang manu-mano. Ano ang maaaring maging isang natatanging identifier ng empleyado? Well, halimbawa, buong pangalan + departamento... gayunpaman, ang isang tao ay may buong pangalan, at may pagkakataon na sila ay magtatrabaho sa parehong departamento, maliit, ngunit mayroon - ibig sabihin ay masama ang desisyon. Posibleng magdagdag ng isang grupo ng iba pang mga patlang, tulad ng petsa ng pag-upa, lungsod, ngunit ang lahat ng ito, tila sa akin, ay masyadong kumplikado ang lohika. Maaari kang magtaka, paano posible na maging kakaiba ang isang grupo ng mga field nang sabay-sabay? sagot ko - siguro. Kung gusto mong malaman, maaari kang mag-google tungkol sa isang bagay tulad ng @Embeddable at @Embedded Well, tapos na tayo sa esensya. Ngayon kailangan namin ng isang simpleng imbakan. Magiging ganito ang hitsura:
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

}
Oo, yun lang. Isang interface lamang, tinawag namin itong EmployeeRepository, pinalawak nito ang JpaRepository na mayroong dalawang naka-type na parameter, ang una ay may pananagutan para sa uri ng data na ginagamit nito, ang pangalawa para sa uri ng key. Sa aming kaso, ito ay Empleyado at Long. Tama na sa ngayon. Ang huling pagpindot bago ilunsad ang application ay ang aming serbisyo:
@Service
@RequiredArgsConstructor
public class EmployeeService {

    private final EmployeeRepository employeeRepository;

    public List<Employee> getAllEmployees() {
        return List.of(new Employee().setId(123L));
    }
}
Mayroon nang pamilyar na RequiredArgsConstructor at ang bagong @Service annotation - ito ang karaniwang tumutukoy sa business logic layer. Kapag nagpapatakbo ng konteksto ng tagsibol, ang mga klase na minarkahan ng anotasyong ito ay gagawin bilang Beans. Kapag nasa klase ng EmployeeController nagawa namin ang panghuling property na EmployeeService at naka-attach ang RequiredArgsConstructor (o gumawa ng constructor sa pamamagitan ng kamay) Spring, kapag sinimulan ang application, mahahanap nito ang lugar na ito at ilalagay kami ng class object sa variable na ito. Ang default dito ay Singleton - i.e. magkakaroon ng isang bagay para sa lahat ng naturang mga link; ito ay mahalagang isaalang-alang kapag nagdidisenyo ng application. Sa totoo lang, iyon lang, maaaring ilunsad ang application. Huwag kalimutang ipasok ang mga kinakailangang setting sa config. REST API o isa pang pagsubok na gawain.  - 4 Hindi ko ilalarawan kung paano mag-install ng database, lumikha ng isang user at ang database mismo, ngunit mapapansin ko lang na sa URL ay gumagamit ako ng dalawang karagdagang mga parameter - useUnicore=true at characterEncoding=UTF-8. Ginawa ito upang ang teksto ay maipakita nang higit pa o hindi gaanong pantay sa anumang sistema. Gayunpaman, kung ikaw ay masyadong tamad na mag-usap-usap sa database at talagang gusto mong sundutin ang gumaganang code, mayroong isang mabilis na solusyon: 1. Idagdag ang sumusunod na dependency sa build.gradle:
implementation 'com.h2database:h2:2.1.214'
2. Sa application.yml kailangan mong i-edit ang ilang mga katangian, magbibigay ako ng kumpletong halimbawa ng seksyon ng tagsibol para sa pagiging simple:
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:
Ang database ay maiimbak sa folder ng proyekto, sa isang file na tinatawag na mydb . Ngunit irerekomenda ko ang pag-install ng isang ganap na database 😉 Kapaki-pakinabang na artikulo sa paksa: Spring Boot With H2 Database Kung sakali, magbibigay ako ng buong bersyon ng aking build.gradle upang maalis ang mga pagkakaiba sa mga dependency:
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()
}
Handa nang ilunsad ang system: REST API o isa pang pagsubok na gawain.  - 5 Maaari mo itong suriin sa pamamagitan ng pagpapadala ng kahilingan sa GET mula sa anumang naaangkop na programa sa aming endpoint. Sa partikular na kaso na ito, gagawin ng isang regular na browser, ngunit sa hinaharap kakailanganin namin ang Postman. REST API o isa pang pagsubok na gawain.  - 6 Oo, sa katunayan, hindi pa namin ipinapatupad ang alinman sa mga kinakailangan sa negosyo, ngunit mayroon na kaming isang application na magsisimula at maaaring palawakin sa kinakailangang paggana. Ipinagpatuloy: REST API at Data Validation
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION