JavaRush /Блоги Java /Random-TG /REST API ва тасдиқи маълумот
Денис
Сатҳи
Киев

REST API ва тасдиқи маълумот

Дар гурӯҳ нашр шудааст
Истинод ба қисми аввал: REST API ва вазифаи санҷишии навбатӣ Хуб, замимаи мо кор мекунад, мо метавонем аз он як навъ посух гирем, аммо ин ба мо чӣ медиҳад? Вай ягон кори фоиданок намекунад. Дере нагузашта гуфт, ки як чизи муфидро амалӣ кунем. Пеш аз ҳама, биёед ба build.gradle-и худ чанд вобастагии нав илова кунем, онҳо барои мо муфид хоҳанд буд:
implementation 'org.apache.commons:commons-lang3:3.12.0'
implementation 'org.apache.commons:commons-collections4:4.4'
implementation 'org.springframework.boot:spring-boot-starter-validation'
Ва мо бо маълумоти воқеие, ки мо бояд коркард кунем, оғоз мекунем. Биёед ба бастаи устувории худ баргардем ва пур кардани an objectро оғоз кунем. Тавре ки шумо дар хотир доред, мо онро танҳо бо як майдон боқӣ гузоштем ва баъд худкор тавассути `@GeneratedValue(strategy = GenerationType.IDENTITY)` тавлид мешавад, Биёед мушаххасоти техникиро аз боби аввал ба ёд орем:
{
  "firstName": String,
  "lastName": String,
  "department": String,
  "salary": String
  "hired": String //"yyyy-mm-dd"
  "tasks": [
  ]
}
Мо бори аввал майдонҳои кофӣ дорем, пас биёед ба татбиқи он шурӯъ кунем. Се майдони аввал саволҳо ба миён намеоранд - ин сатрҳои оддӣ ҳастанд, аммо майдони музди меҳнат аллакай пешниҳод мекунад. Чаро хатти воқеӣ? Дар кори воқеӣ ин ҳам мешавад, муштарӣ ба назди шумо меояд ва мегӯяд - ман мехоҳам ин борро ба шумо фиристам ва шумо онро коркард мекунед. Шумо метавонед, албатта, китф дархам кашед ва ин корро кунед, шумо метавонед кӯшиш кунед, ки ба созиш бирасед ва фаҳмонед, ки маълумотро дар формати зарурӣ интиқол додан беҳтар аст. Биёед тасаввур кунем, ки мо бо муштарии оқил дучор омадем ва розӣ шудем, ки рақамҳоро дар формати ададӣ интиқол додан беҳтар аст ва азбаски сухан дар бораи пул меравад, бигзор он Double бошад. Параметри навбатии сарбории мо санаи ба кор қабул кардан хоҳад буд, муштарӣ онро дар формати мувофиқашуда ба мо мефиристад: yyyy-mm-dd, ки дар он y барои солҳо, m барои рӯзҳо ва d барои рӯзҳо дар назар аст - 2022- 08-12. Майдони охирин дар айни замон рӯйхати вазифаҳои ба муштарӣ таъиншуда хоҳад буд. Аён аст, ки Task як an objectи дигаре дар пойгоҳи додаи мост, аммо мо ҳоло дар бораи он маълумоти зиёде надорем, аз ин рӯ мо an objectи асоситаринро тавре эҷод мекунем, ки қаблан бо Корманд анҷом дода будем. Ягона чизе, ки мо ҳоло тахмин карда метавонем, ин аст, ки ба як корманд зиёда аз як вазифа гузоштан мумкин аст, бинобар ин мо усули ба истилоҳ "Як ба бисёр", таносуби як ба бисёрро татбиқ хоҳем кард. Агар ба таври муфассал сухан ронем, як сабт дар ҷадвали кормандон метавонад ба якчанд сабтҳои ҷадвали вазифаҳо мувофиқат кунад . Ман инчунин қарор додам, ки чунин чизеро ҳамчун майдони ягонаи рақам илова кунам, то мо метавонем як кормандро аз дигараш ба таври равшан фарқ кунем. Дар айни замон синфи Корманди мо чунин менамояд:
@Entity
@Data
@Accessors(chain = true)
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JsonIgnore
    Long id;

    @NotBlank
    @Column(unique = true)
    private String uniqueNumber;
    private String firstName;
    private String lastName;
    private String department;
    private Double salary;
    private LocalDate hired;

    @OneToMany
    @JoinColumn(name = "employee_id")
    List<Task> tasks = new ArrayList<>();
}
Синфи зерин барои an objectи Вазифа сохта шудааст:
@Entity
@Data
@Accessors(chain = true)
public class Task {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long taskId;
}
Тавре ки ман гуфтам, мо дар Task чизи навро намебинем, барои ин синф як анбори нав низ сохта шудааст, ки нусхаи репозиторий барои Корманд аст - ман намедиҳам, шумо метавонед онро худатон бо қиёс созед. Аммо дар бораи синфи Корманд сухан рондан маъно дорад. Тавре ки ман гуфтам, мо якчанд соҳаҳоро илова кардем, аммо ҳоло танҳо охиринаш ҷолиб аст - вазифаҳо. Ин як Рӯйхати<Task> вазифаҳост , он фавран бо ArrayList-и холӣ оғоз карда мешавад ва бо якчанд эзоҳҳо қайд карда мешавад. 1. @OneToMany Тавре ки ман гуфтам, ин таносуби кормандон ба вазифаҳо хоҳад буд. 2. @JoinColumn - сутуне, ки тавассути он an objectҳо ҳамроҳ карда мешаванд. Дар ин ҳолат, дар ҷадвали Вазифаҳо сутуни коргари_id сохта мешавад, ки ба идентификатсияи корманди мо ишора мекунад; он ба мо ҳамчун ForeighnKey хизмат мекунад Сарфи назар аз муқаддас будани ном, шумо метавонед сутунро ҳар чизе ки ба шумо маъқул аст, номгузорӣ кунед. Вазъият каме мураккабтар хоҳад шуд, агар ба шумо на танҳо ID, балки як намуди сутуни воқеиро истифода баред; мо баъдтар ба ин мавзӯъ дахл хоҳем кард. 3. Шумо инчунин метавонед тавзеҳи навро дар болои id мушоҳида карда бошед - @JsonIgnore. Азбаски id як ҷузъи дохorи мост, ба мо ҳатман лозим нест, ки онро ба муштарӣ баргардонем. 4. @NotBlank як тавзеҳи махсус барои тасдиқ аст, ки мегӯяд, ки майдон набояд нул ё сатри холӣ бошад 5. @Column(нодир = ҳақиқӣ) мегӯяд, ки ин сутун бояд арзишҳои беназир дошта бошад. Ҳамин тариқ, мо аллакай ду an object дорем, онҳо ҳатто ба ҳамдигар пайвастанд. Вақти он расидааст, ки онҳоро ба барномаи мо ворид кунем - биёед бо хидматҳо ва контроллерҳо сарукор кунем. Пеш аз ҳама, биёед ноустувории худро аз усули getAllEmployees() хориҷ кунем ва онро ба чизе табдил диҳем, ки воқеан кор мекунад:
public List<Employee> getAllEmployees() {
       return employeeRepository.findAll();
   }
Ҳамин тариқ, анбори мо ҳама чизеро, ки аз пойгоҳи додаҳо дастрас аст, ҷамъоварӣ мекунад ва ба мо медиҳад. Қобor зикр аст, ки он инчунин рӯйхати вазифаҳоро интихоб мекунад. Аммо баровардани он бешубҳа ҷолиб аст, аммо агар дар он ҷо ҳеҷ чиз набошад, он чӣ аст? Ин дуруст аст, ин маънои онро дорад, ки мо бояд бифаҳмем, ки чӣ гуна чизеро дар он ҷо гузорем. Пеш аз хама, биёед дар контроллерамон усули нав нависем.
@PostMapping("${application.endpoint.employee}")
    public ResponseEntity<?> createOrUpdateEmployee(@RequestBody final Employee employee) {
        try {
            employeeService.save(employee);
        } catch (ValidationException e) {
            return ResponseEntity.badRequest().body(e.getViolations());
        }
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
Ин @PostMapping аст, яъне. он дархостҳои POST-ро, ки ба нуқтаи ниҳоии кормандони мо меоянд, коркард мекунад. Умуман, ман фикр мекардам, ки азбаски ҳама дархостҳо ба ин контроллер ба як нуқтаи ниҳоӣ меоянд, биёед инро каме содда кунем. Танзимоти хуби моро дар application.yml дар хотир доред? Биёед онҳоро ислоҳ кунем. Бигзор қисмати барнома ҳоло чунин бошад:
application:
  endpoint:
    root: api/v1
    employee: ${application.endpoint.root}/employees
    task: ${application.endpoint.root}/tasks
Ин ба мо чӣ медиҳад? Далели он, ки дар контроллер мо метавонем харитасозии ҳар як усули мушаххасро хориҷ кунем ва нуқтаи ниҳоӣ дар сатҳи синф дар шарҳи @RequestMapping("${application.endpoint.employee}") муқаррар карда мешавад . Ин зебоӣ ҳоло дар он аст. Назоратчии мо:
@RestController
@RequestMapping("${application.endpoint.employee}")
@RequiredArgsConstructor
public class EmployeeController {

    private final EmployeeService employeeService;

    @GetMapping
    public ResponseEntity<List<Employee>> getEmployees() {
        return ResponseEntity.ok().body(employeeService.getAllEmployees());
    }

    @PostMapping
    public ResponseEntity<?> createOrUpdateEmployee(@RequestBody final Employee employee) {
        try {
            employeeService.save(employee);
        } catch (ValidationException e) {
            return ResponseEntity.badRequest().body(e.getViolations());
        }
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
}
Бо вуҷуди ин, биёед идома диҳем. Дар усули createOrUpdateEmployee маҳз чӣ рӯй медиҳад? Аён аст, ки staffeService мо усули сарфаро дорад, ки бояд барои тамоми корҳои сарфакорӣ масъул бошад. Инчунин маълум аст, ки ин усул метавонад истисноро бо номи худфаъолкунанда гузорад. Онхое. як навъ валидкунй гузаронда мешавад. Ва ҷавоб мустақиман аз натиҷаҳои санҷиш вобаста аст, оё он 201 Created ё 400 badRequest бо рӯйхати хатогиҳо хоҳад буд. Ба пеш нигоҳ карда, ин хидмати нави тасдиқи мост, он маълумоти воридшударо барои мавҷудияти майдонҳои зарурӣ тафтиш мекунад (дар хотир доред @NotBlank?) ва қарор мекунад, ки ин маълумот барои мо мувофиқ аст ё не. Пеш аз гузаштан ба усули сарфакунӣ, биёед ин хидматро тасдиқ ва татбиқ кунем. Барои ин ман пешниҳод мекунам, ки бастаи алоҳидаи тасдиқкунӣ эҷод карда шавад, ки дар он мо хидмати худро ҷойгир мекунем.
import com.example.demo.exception.ValidationException;
import com.example.demo.persistence.entity.Employee;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;

@Service
@RequiredArgsConstructor
public class ValidationService {

    private final Validator validator;

    public boolean isValidEmployee(Employee employee) throws ValidationException {
        Set<Constraintviolation<Employee>> constraintViolations = validator.validate(employee);

        if (CollectionUtils.isNotEmpty(constraintViolations)) {
            throw new ValidationException(buildViolationsList(constraintViolations));
        }
        return true;
    }

    private <T> List<Violation> buildViolationsList(Set<Constraintviolation<T>> constraintViolations) {
        return constraintViolations.stream()
                                   .map(violation -> new Violation(
                                                   violation.getPropertyPath().toString(),
                                                   violation.getMessage()
                                           )
                                   )
                                   .toList();
    }
}
Синф хеле калон шуд, аммо воҳима накунед, мо инро ҳозир мефаҳмем :) Дар ин ҷо мо асбобҳои китобхонаи тасдиқи тайёри javax.validation -ро истифода мебарем Ин китобхона ба мо аз вобастагии наве омад, ки мо илова ба build.graddle татбиқи 'org.springframework.boot:spring-boot-starter -validation' Дӯстони деринаи мо Service and RequiredArgsConstructor Аллакай ба мо ҳама чизеро, ки мо бояд дар бораи ин синф донем, бигӯед, инчунин майдони хусусии тасдиқкунандаи ниҳоӣ мавҷуд аст. Ӯ ҷоду хоҳад кард. Мо усули isValidEmployee-ро офаридаем, ки ба он мо метавонем an objectи Кормандро гузаронем; ин усул ValidationException-ро мепартояд, ки мо онро каме дертар менависем. Бале, ин як истиснои фармоишӣ барои ниёзҳои мо хоҳад буд. Бо истифода аз усули validator.validate(корманд), мо рӯйхати an objectҳои ConstraintViolation-ро мегирем - ҳамаи он номутобиқатӣ бо эзоҳҳои тасдиқ, ки қаблан илова карда будем. Мантиқи минбаъда оддӣ аст, агар ин рӯйхат холӣ набошад (яъне қонуншиканиҳо вуҷуд дошта бошанд), мо истисно мегузорем ва рӯйхати вайронкуниҳоро месозем - усули buildViolationsList Лутфан қайд кунед, ки ин усули умумӣ аст, яъне. метавонад бо рӯйхати вайронкуниҳои an objectҳои гуногун кор кунад - он метавонад дар оянда муфид бошад, агар мо чизи дигареро тасдиқ кунем. Ин усул воқеан чӣ кор мекунад? Бо истифода аз API ҷараён, мо аз рӯйхати қонуншиканиҳо мегузарем. Мо ҳар як вайронкуниро дар усули харита ба an objectи вайронкунии шахсии худ табдил медиҳем ва ҳама an objectҳои натиҷаро дар рӯйхат ҷамъ меорем. Мо ӯро бармегардонем. Боз чӣ an objectи вайронкунии худи мост, шумо мепурсед? Ана як сабти оддӣ
public record Violation(String property, String message) {}
Сабтҳо чунин навовариҳои махсус дар Java мебошанд, агар ба шумо an objectи дорои маълумот бидуни мантиқ ё чизи дигар лозим бошад. Гарчанде ки ман худам то ҳол нафаҳмидам, ки чаро ин корро кардааст, баъзан ин як чизи хеле қулай аст. Он бояд дар файли алоҳида, ба мисли синфи муқаррарӣ сохта шавад. Бозгашт ба ValidationException фармоишӣ - чунин менамояд:
@RequiredArgsConstructor
public class ValidationException extends Exception {

    @Getter
    private final List<Violation> violations;
}
Он рӯйхати ҳама вайронкуниҳоро нигоҳ медорад, шарҳи Lombok - Getter ба рӯйхат замима карда мешавад ва тавассути шарҳи дигари Lombok мо конструктори лозимиро "амалӣ" кардем :) Дар ин ҷо бояд қайд кард, ки ман рафтори isValid-ро дуруст иҷро намекунам. ... усул, он ё ҳақиқӣ ё истисноро бармегардонад, аммо он мебуд, ки худро бо False муқаррарӣ маҳдуд кунем. Равиши истисно аз он сабаб сурат мегирад, ки ман мехоҳам ин хаторо ба муштарӣ баргардонам, яъне маънои онро дорад, ки ман бояд чизи дигареро аз усули булӣ баргардонам. Дар мавриди усулҳои тасдиқи сирф дохилӣ, истисно кардан лозим нест; дар ин ҷо сабти ном лозим мешавад. Аммо, биёед ба EmployeeService-и худ баргардем, мо ҳоло ҳам бояд ба захира кардани an objectҳо шурӯъ кунем :) Биёед бубинем, ки ин синф ҳоло чӣ гуна аст:
@Service
@RequiredArgsConstructor
public class EmployeeService {

    private final EmployeeRepository employeeRepository;
    private final ValidationService validationService;

    public List<Employee> getAllEmployees() {
        return employeeRepository.findAll();
    }

    @Transactional
    public void save(Employee employee) throws ValidationException {
        if (validationService.isValidEmployee(employee)) {
            Employee existingEmployee = employeeRepository.findByUniqueNumber(employee.getUniqueNumber());
            if (existingEmployee == null) {
                employeeRepository.save(employee);
            } else {
                existingEmployee = updateFields(existingEmployee, employee);
                employeeRepository.save(existingEmployee);
            }
        }
    }

    private Employee updateFields(Employee existingEmployee, Employee updatedEmployee) {
        return existingEmployee.setDepartment(updatedEmployee.getDepartment())
                               .setSalary(updatedEmployee.getSalary())
            				 //TODO implement tasks merging instead of replacement
                               .setTasks(updatedEmployee.getTasks());
    }
}
Аҳамият диҳед, ки моликияти нави ниҳоии хусусии ValidationService validationService; Худи усули нигоҳдорӣ бо шарҳи @Transactional ишора шудааст, то ки агар RuntimeException гирифта шавад, тағирот баргардонида мешавад. Пеш аз ҳама, мо маълумоти воридшударо бо истифода аз хидмате, ки мо навиштем, тасдиқ мекунем. Агар ҳама чиз осон бошад, мо тафтиш мекунем, ки оё дар базаи маълумот корманд мавҷуд аст (бо истифода аз рақами ягона). Агар не, мо навашро захира мекунем, агар мавҷуд бошад, майдонҳоро дар синф навсозӣ мекунем. Оре, мо воқеан чӣ гуна тафтиш мекунем? Бале, ин хеле содда аст, мо ба анбори кормандон усули нав илова кардем:
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
    Employee findByUniqueNumber(String uniqueNumber);
}
Чӣ назаррас аст? Ман ягон дархости мантиқӣ ё SQL нанавиштам, гарчанде ки он дар ин ҷо дастрас аст. Баҳор, танҳо бо хондани номи усул, муайян мекунад, ки ман чӣ мехоҳам - ByUniqueNumber -ро пайдо кунед ва сатри мувофиқро ба усул гузаронед. Бозгашт ба навсозии майдонҳо - дар ин ҷо ман қарор додам, ки ақли солимро истифода барам ва танҳо шӯъба, маош ва вазифаҳоро навсозӣ кунам, зеро тағир додани ном, гарчанде ки чизи қобor қабул аст, то ҳол он қадар маъмул нест. Ва тағир додани санаи ба кор қабул кардан умуман як масъалаи баҳсбарангез аст. Дар ин ҷо чӣ кор кардан хуб мебуд? Рӯйхати вазифаҳоро муттаҳид кунед, аммо азбаски мо ҳоло вазифа надорем ва намедонем, ки чӣ гуна онҳоро фарқ кунем, мо TODO-ро тарк мекунем. Биёед кӯшиш кунем, ки Франкенштейни худро оғоз кунем. Агар ман тавсиф кардани чизеро фаромӯш накарда бошам, он бояд кор кунад, аммо аввал ин дарахти синфест, ки мо гирифтем: REST API ва тасдиқи маълумот - 1 Синфҳои тағир додашуда бо ранги кабуд, синфҳои нав бо ранги сабз нишон дода мешаванд, чунин нишондодҳоро метавон ба даст овард, агар шумо кор кунед бо анбори git, аммо git мавзӯи мақолаи мо нест, бинобар ин мо дар ин бора таваққуф намекунем. Ҳамин тавр, дар айни замон мо як нуқтаи ниҳоӣ дорем, ки ду усули GET ва POST -ро дастгирӣ мекунад. Дар омади гап, баъзе маълумоти ҷолиб дар бораи нуқтаи ниҳоӣ. Чаро, масалан, мо барои GET ва POST нуқтаҳои алоҳида ҷудо накардаем, ба монанди getAllEmployees ё createEmployees? Ҳама чиз бениҳоят содда аст - доштани як нуқтаи ягона барои ҳама дархостҳо хеле қулайтар аст. Масири масир дар асоси усули HTTP сурат мегирад ва интуитивист, лозим нест, ки ҳамаи вариантҳои getAllEmployees, getEmployeeByName, даст... навсозӣ... эҷод... нест кунед... Биёед он чизеро, ки гирифтаем, санҷем. Ман аллакай дар мақолаи қаблӣ навишта будам, ки ба мо Postman лозим аст ва вақти насб кардани он расидааст. Дар интерфейси барнома, мо дархости нави POST эҷод мекунем REST API ва тасдиқи маълумот - 2 ва кӯшиш мекунем, ки онро фиристем. Агар ҳама чиз хуб бошад, мо Status 201-ро дар тарафи рости экран мегирем. Аммо, масалан, бо фиристодани ҳамон чизе, вале бе рақами беназир (ки мо тасдиқ дорем), ман ҷавоби дигар мегирам: Хуб REST API ва тасдиқи маълумот - 3 , биёед тафтиш кунем, ки интихоби пурраи мо чӣ гуна кор мекунад - мо барои як нуқтаи ниҳоӣ усули GET эҷод мекунем ва онро мефиристем. . REST API ва тасдиқи маълумот - 4 Ман самимона умедворам, ки ҳама чиз барои шумо низ ҳамон тавре ки барои ман буд, кор кард ва дар мақолаи навбатӣ дидор шавед .
Шарҳҳо
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION