# თავი 6: RESTful Web-სერვისების აგება --- ## 6.1 Server-Rendered გვერდებიდან API-მდე წინა თავებში ჩვენ ავაგეთ ვებ-აპლიკაცია, რომელიც HTML-ის გენერირებას სერვერზე ახდენდა — controller-ები ამუშავებდნენ მოთხოვნებს, ავსებდნენ model-ს და აბრუნებდნენ FreeMarker-ის template-ს, რომელსაც ბრაუზერი გამოსახავდა. ეს მიდგომა კარგად მუშაობს მაშინ, როცა სერვერი სრულად აკონტროლებს მომხმარებლის გამოცდილებას. თუმცა, თანამედროვე აპლიკაციებს ხშირად სჭირდებათ მონაცემების მიწოდება ისეთი კლიენტებისთვის, რომლებიც არ არიან ბრაუზერები — მობილური აპლიკაციები, single-page JavaScript (SPA) აპლიკაციები, სხვა backend სერვისები თუ IoT მოწყობილობები. ამ კლიენტებს არ სჭირდებათ HTML. მათ სურთ სტრუქტურირებული მონაცემები, როგორც წესი JSON, რათა თავად გადაწყვიტონ მათი დამუშავება და ვიზუალიზაცია. ეს თავი გაგაცნობთ RESTful ვებ-სერვისებს — HTTP-ზე დაფუძნებულ API-ებს, რომლებიც თქვენი აპლიკაციის მონაცემებსა და ოპერაციებს ხელმისაწვდომს ხდის ნებისმიერი კლიენტისთვის, რომელსაც HTTP პროტოკოლით კომუნიკაცია შეუძლია. ჩვენ ვისწავლით REST-ის პრინციპებს, REST controller-ების შექმნას Spring Boot-ის გამოყენებით, სხვადასხვა ტიპის შემავალი მონაცემების დამუშავებას (path variables, query parameters, request bodies), API პასუხების ფორმირებას Data Transfer Objects (DTOs) საშუალებით, JSON სერიალიზაციის კონტროლს Jackson-ით, მნიშვნელოვანი HTTP status code-ების დაბრუნებას `ResponseEntity`-ს მეშვეობით, შეცდომების ერთიან მართვას და API-ს დოკუმენტირებას SpringDoc OpenAPI-ით. --- ## 6.2 რა არის Web-სერვისები? Web-სერვისი არის პროგრამული სისტემა, რომელიც ფუნქციონალს ქსელის საშუალებით, სტანდარტული პროტოკოლების გამოყენებით აქვეყნებს. პრაქტიკული თვალსაზრისით, ეს არის endpoint-ების ერთობლიობა, რომელთა გამოძახება სხვა პროგრამას შეუძლია — არა ბრაუზერში ლინკზე დაჭერით, არამედ პროგრამულად HTTP მოთხოვნის გაგზავნით. წლების განმავლობაში ვებ-სერვისებში ორი არქიტექტურული სტილი დომინირებდა: **SOAP** და **REST**. ### SOAP (მოკლე მიმოხილვა) SOAP (Simple Object Access Protocol) არის XML-ზე დაფუძნებული პროტოკოლი მკაცრი სტანდარტებით, რომლებიც განსაზღვრულია W3C-ის მიერ. ის იყენებს XML-ს ყველა შეტყობინებისთვის, მხარს უჭერს მრავალ სატრანსპორტო პროტოკოლს (HTTP, SMTP, TCP) და ეყრდნობა ფორმალურ კონტრაქტს (WSDL) სერვისის ინტერფეისის აღსაწერად. SOAP ძლიერია კორპორაციულ (enterprise) კონტექსტში, სადაც საჭიროა ტრანზაქციული გარანტიები და პროტოკოლის დონის უსაფრთხოება (WS-Security). თუმცა, მისი სირთულე და მოცულობითი სტრუქტურა ნაკლებად გამოსადეგია თანამედროვე მობილური და ვებ-აპლიკაციებისთვის. ### REST REST (Representational State Transfer) არის არქიტექტურული სტილი — და არა პროტოკოლი — რომელიც განსაზღვრულია შეზღუდვების (constraints) ერთობლიობით. მათი დაცვა უზრუნველყოფს სერვისების სიმარტივეს, მასშტაბირებადობას და HTTP პროტოკოლთან სრულ თავსებადობას. REST გახდა დომინანტური მიდგომა ვებ-API-ების შესაქმნელად და სწორედ მას გამოვიყენებთ ამ თავში. --- ## 6.3 REST პრინციპები RESTful სერვისი რამდენიმე ძირითად პრინციპს მიჰყვება: **რესურსების იდენტიფიცირება URI-ებით.** ყოველი მონაცემი, რომელსაც თქვენი API აქვეყნებს — მომხმარებელი, პროდუქტი თუ პოსტი — არის რესურსი და თითოეულ მათგანს აქვს უნიკალური URI: `/users/42`, `/products/7`, `/posts/15/comments`. **სტანდარტული HTTP მეთოდები.** პერსონალური ოპერაციების გამოგონების ნაცვლად, REST იყენებს არსებულ HTTP მეთოდებს: GET წამოსაღებად, POST შესაქმნელად, PUT ჩასანაცვლებლად, PATCH ნაწილობრივი განახლებისთვის, DELETE წასაშლელად. **Stateless კომუნიკაცია.** თითოეული მოთხოვნა შეიცავს ყველა იმ ინფორმაციას, რაც სერვერს მისი დამუშავებისთვის სჭირდება. სერვერს არ ახსოვს არაფერი წინა მოთხოვნების შესახებ. ეს აადვილებს სერვისის მასშტაბირებას (scaling). **Client-server განცალკევება.** კლიენტი და სერვერი დამოუკიდებელნი არიან. სერვერი აქვეყნებს API-ს, კლიენტი კი მას მოიხმარს. არცერთი მათგანი არ არის დამოკიდებული მეორის იმპლემენტაციის დეტალებზე. **JSON მონაცემთა ფორმატი.** მიუხედავად იმისა, რომ REST არ ავალდებულებს კონკრეტულ ფორმატს, JSON გახდა უნივერსალური სტანდარტი REST API-ებისთვის მისი სიმსუბუქისა და თავსებადობის გამო. კლიენტი REST API-სთან ურთიერთქმედებს სტანდარტული HTTP ხელსაწყოებით. Command line-იდან: ```bash curl -X GET http://api.example.com/users/1 ``` JavaScript-იდან: ```javascript fetch('http://api.example.com/users/1') .then(response => response.json()) .then(data => console.log(data)); ``` --- ## 6.4 HTTP მეთოდები და მათი სემანტიკა REST API-ში თითოეულ მეთოდს თავისი დანიშნულება აქვს: | მეთოდი | დანიშნულება | ტიპური გამოყენება | |--------|---------|---------------| | **GET** | რესურსის ან კოლექციის წამოღება | `GET /users` — სია; `GET /users/1` — კონკრეტული იუზერი | | **POST** | ახალი რესურსის შექმნა | `POST /users` — ახალი იუზერის შექმნა | | **PUT** | რესურსის სრული ჩანაცვლება | `PUT /users/1` — იუზერ 1-ის სრული განახლება | | **PATCH** | რესურსის ნაწილობრივი განახლება | `PATCH /users/1` — კონკრეტული ველების შეცვლა | | **DELETE** | რესურსის წაშლა | `DELETE /users/1` — იუზერ 1-ის წაშლა | GET და DELETE მეთოდებს, როგორც წესი, არ აქვთ request body. POST, PUT და PATCH აგზავნიან მონაცემებს body-ში (ძირითადად JSON-ის სახით). GET არის უსაფრთხო (safe) და იდემპოტენტური (idempotent). PUT და DELETE იდემპოტენტურია. POST არცერთი მათგანი არ არის. --- ## 6.5 RESTful URI-ების დაპროექტება კარგი URI დიზაინი API-ს ინტუიციურს ხდის. ძირითადი კონვენციებია: **გამოიყენეთ არსებითი სახელები და არა ზმნები.** URI აღნიშნავს რესურსს — რა არის ის, და არა იმას, თუ რას აკეთებთ მასთან. HTTP მეთოდი განსაზღვრავს მოქმედებას. `/users` სწორია; `/getUsers` — არა. **კოლექციებისთვის გამოიყენეთ მრავლობითი რიცხვი.** `/users`, `/posts` — და არა `/user` ან `/post`. **იერარქიული გზები ჩადგმული რესურსებისთვის.** თუ კომენტარები ეკუთვნის პოსტს: `/posts/{postId}/comments`. **გამოიყენეთ lowercase და ტირეები.** თუ რესურსის სახელი რამდენიმე სიტყვისგან შედგება: `/blog-posts`. ბლოგის აპლიკაციის URI დიზაინის მაგალითი: ``` GET /posts — პოსტების სია GET /posts/{postId} — კონკრეტული პოსტი POST /posts — ახალი პოსტი PUT /posts/{postId} — პოსტის განახლება DELETE /posts/{postId} — პოსტის წაშლა GET /posts/{postId}/comments — პოსტის კომენტარები POST /posts/{postId}/comments — კომენტარის დამატება ``` --- ## 6.6 REST Controller-ების აგება @RestController-ით იმისათვის, რომ შევქმნათ REST API, ვიყენებთ `@RestController` ანოტაციას. ის აერთიანებს `@Controller`-სა და `@ResponseBody`-ს, რაც ნიშნავს, რომ handler მეთოდის მიერ დაბრუნებული მნიშვნელობა პირდაპირ სერიალიზდება HTTP response body-ში (ნაგულისხმევად JSON-ად). ```java @RestController @RequestMapping("/api/users") public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @GetMapping public List getAllUsers() { return userService.findAll(); } @PostMapping public User createUser(@RequestBody User newUser) { return userService.save(newUser); } } ``` --- ## 6.7 Path Variables, Query Parameters და Request Bodies REST controller-ები მონაცემებს სამი გზით იღებენ: ### Path Variables გამოიყენება კონკრეტული რესურსის იდენტიფიცირებისთვის: ```java @GetMapping("/users/{id}") public User getUser(@PathVariable Long id) { return userService.findById(id); } ``` ### Query Parameters გამოიყენება ფილტრაციისთვის, ძებნისთვის ან პაგინაციისთვის: ```java @GetMapping("/users") public List searchUsers( @RequestParam(required = false) String name, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size) { return userService.search(name, page, size); } ``` ### Request Bodies გამოიყენება რესურსის შესაქმნელად ან სრულად განსაახლებლად. `@RequestBody` ეუბნება Spring-ს, რომ მოახდინოს JSON-ის დესერიალიზაცია Java ობიექტში Jackson-ის მეშვეობით. --- ## 6.8 DTO-ების გამოყენება API პასუხების ფორმირებისთვის Entity ობიექტების პირდაპირ დაბრუნება ცუდი პრაქტიკაა, რადგან მათ შეიძლება შეიცავდნენ სენსიტიურ ინფორმაციას (მაგ. პაროლები) ან გამოიწვიონ infinite recursion სერიალიზაციისას. **Data Transfer Objects (DTOs)** ამ პრობლემას აგვარებს API-სთვის ცალკეული კლასის შექმნით. ### DTO-ს გამოყენების მაგალითი: ```java public class UserDTO { private Long id; private String name; private String email; public UserDTO(User user) { this.id = user.getId(); this.name = user.getName(); this.email = user.getEmail(); } } ``` Controller-ში ჩვენ Entity-ს ვაკონვერტირებთ DTO-ში: ```java @GetMapping("/users/{id}") public UserDTO getUser(@PathVariable Long id) { User user = userService.findById(id); return new UserDTO(user); } ``` --- ## 6.9 JSON სერიალიზაციის კონტროლი Jackson-ით Spring Boot იყენებს **Jackson** ბიბლიოთეკას კონვერტაციისთვის. Spring Boot 4-ში Jackson 3 არის ნაგულისხმევი. ### ძირითადი ანოტაციები: | ანოტაცია | დანიშნულება | |------------|---------| | `@JsonProperty("name")` | ველის სახელის შეცვლა JSON-ში | | `@JsonIgnore` | ველის გამოტოვება | | `@JsonFormat(pattern = "yyyy-MM-dd")` | თარიღის ფორმატირება | --- ## 6.10 პასუხების კონტროლი ResponseEntity-ით `ResponseEntity` გაძლევთ საშუალებას სრულად აკონტროლოთ HTTP პასუხი — status code, header-ები და body. ```java @GetMapping("/users/{id}") public ResponseEntity getUser(@PathVariable Long id) { return userService.findById(id) .map(user -> ResponseEntity.ok(new UserDTO(user))) .orElse(ResponseEntity.notFound().build()); } ``` --- ## 6.11 შეცდომების მართვა (Error Handling) პროფესიონალურ API-ში შეცდომები უნდა ბრუნდებოდეს სტრუქტურირებული სახით. `@RestControllerAdvice` გამოიყენება გლობალური გამონაკლისების (exceptions) დასამუშავებლად. ```java @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity handleResourceNotFound(ResourceNotFoundException ex) { ErrorResponse error = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage()); return new ResponseEntity<>(error, HttpStatus.NOT_FOUND); } } ``` --- ## 6.12 API დოკუმენტირება SpringDoc OpenAPI-ით **SpringDoc OpenAPI** ავტომატურად აგენერირებს ინტერაქტიულ დოკუმენტაციას. `pom.xml`-ში დამოკიდებულების დამატების შემდეგ, **Swagger UI** ხელმისაწვდომი ხდება `http://localhost:8080/swagger-ui.html` მისამართზე. --- ## 6.13 API-ს ტესტირება: curl და Postman API-ს შესამოწმებლად გამოიყენება: * **curl**: ტერმინალიდან მოთხოვნების გასაგზავნად. * **Postman**: გრაფიკული ინტერფეისი რთული მოთხოვნებისა და ტესტების მართვისთვის. --- ## შეჯამება ამ თავში ჩვენ განვიხილეთ Spring Boot-ით RESTful სერვისების აგების ყველა ასპექტი: URI დიზაინი, `@RestController`-ების გამოყენება, DTO-ების როლი, Jackson-ით სერიალიზაცია, `ResponseEntity`-ით პასუხების მართვა და `SpringDoc`-ით დოკუმენტირება. ეს ცოდნა საფუძველია თანამედროვე, მასშტაბირებადი სისტემების შესაქმნელად.