← Назад к вопросам

Какая главная цель REST?

1.0 Junior🔥 271 комментариев
#REST API и микросервисы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Главная цель REST

REST (Representational State Transfer) — это архитектурный стиль для проектирования распределённых систем. Рассскажу о его основной цели, принципах и как это применяется на практике.

Главная цель REST

Главная цель REST:

Обеспечить простой, масштабируемый и надёжный способ
взаимодействия между клиентом и сервером через HTTP
посредством управления ресурсами и их состояниями.

Разложу по составляющим:

1. Простота и Единообразие

REST использует:

  • Стандартные HTTP методы (GET, POST, PUT, DELETE, PATCH)
  • Единообразный интерфейс
  • Предсказуемые URL структуры
❌ Сложно (RPC style):
GET /api/users/createUser?id=123&name=John
GET /api/users/updateUser?id=123&name=Jane
GET /api/users/deleteUser?id=123
GET /api/users/getUser?id=123

✅ Просто (REST):
GET /api/users/123          # Получить пользователя
POST /api/users             # Создать пользователя
PUT /api/users/123          # Обновить пользователя
DELETE /api/users/123       # Удалить пользователя
PATCH /api/users/123        # Частичное обновление

2. Масштабируемость

REST позволяет:

  • Легко добавлять новые ресурсы
  • Кэшировать ответы
  • Распределить нагрузку
  • Использовать CDN
@RestController
@RequestMapping("/api/v1")
public class ResourceController {
    
    // GET /api/v1/users         → получить список
    @GetMapping("/users")
    public List<User> listUsers() {
        return userService.findAll();
    }
    
    // GET /api/v1/users/123     → получить конкретного
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
    
    // POST /api/v1/users        → создать новый
    @PostMapping("/users")
    public User createUser(@RequestBody UserRequest request) {
        return userService.create(request);
    }
    
    // PUT /api/v1/users/123     → полное обновление
    @PutMapping("/users/{id}")
    public User updateUser(
            @PathVariable Long id,
            @RequestBody UserRequest request) {
        return userService.update(id, request);
    }
    
    // DELETE /api/v1/users/123  → удаление
    @DeleteMapping("/users/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.delete(id);
    }
}

3. Стандартизация HTTP

REST придерживается правил HTTP:

МетодИдемпотентныйБезопасныйИспользование
GETЧтение данных
POSTСоздание ресурса
PUTПолное обновление
PATCHЧастичное обновление
DELETEУдаление
HEADМетаданные без тела

Идемпотентность важна:

// ✓ Идемпотентно - можно вызвать 100x без проблем
GET /api/users/123
PUT /api/users/123 (с теми же данными)
DELETE /api/users/123 (второй раз вернёт 404 или успех)

// ✗ Не идемпотентно - каждый вызов создаёт новый ресурс
POST /api/users
POST /api/users
POST /api/users  // 3 разных пользователя!

4. Statelessness (Отсутствие состояния)

Главный принцип REST:

Каждый запрос содержит всю информацию
Для его обработки. Сервер не хранит контекст клиента.
// ❌ Неправильно (stateful)
@RestController
public class BadController {
    private Map<Long, User> sessions = new HashMap<>();
    
    @PostMapping("/login")
    public void login(@RequestBody Credentials creds) {
        User user = authenticate(creds);
        sessions.put(user.getId(), user);  // Хранит состояние!
    }
    
    @GetMapping("/profile")
    public User getProfile() {
        // Зависит от предыдущего login()!
        // Если распределено на несколько серверов - не работает
        return sessions.get(getCurrentUserId());
    }
}

// ✅ Правильно (stateless)
@RestController
public class GoodController {
    
    @PostMapping("/login")
    public TokenResponse login(@RequestBody Credentials creds) {
        User user = authenticate(creds);
        String token = jwtProvider.generate(user);  // JWT Token
        return new TokenResponse(token);
    }
    
    @GetMapping("/profile")
    public User getProfile(
            @RequestHeader("Authorization") String token) {
        // Каждый запрос содержит token
        // Можно вызвать с любого сервера
        User user = jwtProvider.verify(token);
        return user;
    }
}

Преимущества:

  • Горизонтальное масштабирование (load balancer может отправить на любой сервер)
  • Нет стейт-менеджмента на сервере
  • Лучше переносит перезагрузки

5. Кэширование

REST поддерживает HTTP кэширование:

@RestController
@RequestMapping("/api/v1")
public class CacheableController {
    
    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUser(
            @PathVariable Long id,
            HttpServletResponse response) {
        User user = userService.findById(id);
        
        // Кэшировать на 1 час
        response.setHeader("Cache-Control", "public, max-age=3600");
        response.setHeader("ETag", calculateETag(user));
        
        return ResponseEntity.ok(user);
    }
    
    @GetMapping("/news")
    public ResponseEntity<List<News>> getNews(
            HttpServletResponse response) {
        // Постоянно меняющиеся данные - не кэшировать
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        
        return ResponseEntity.ok(newsService.findRecent());
    }
}

Кэширование может быть:

  • На клиенте (браузер)
  • На промежуточном сервере (CDN, proxy)
  • На сервере (HTTP кэш)

6. Ресурсоориентированность

Суть REST: всё это ресурсы

Пользователи:
GET /api/users              → Список пользователей
GET /api/users/123          → Пользователь 123
GET /api/users/123/posts    → Посты пользователя 123

Посты:
GET /api/posts              → Список постов
GET /api/posts/456          → Пост 456
GET /api/posts/456/comments → Комментарии к посту 456

Комментарии:
GET /api/comments           → Список комментариев
DELETE /api/comments/789    → Удалить комментарий
@RestController
@RequestMapping("/api/v1")
public class ResourceController {
    
    // Вложенные ресурсы
    @GetMapping("/users/{userId}/posts")
    public List<Post> getUserPosts(@PathVariable Long userId) {
        return postService.findByUserId(userId);
    }
    
    @GetMapping("/users/{userId}/posts/{postId}")
    public Post getUserPost(
            @PathVariable Long userId,
            @PathVariable Long postId) {
        return postService.findById(postId, userId);
    }
    
    @PostMapping("/users/{userId}/posts/{postId}/comments")
    public Comment addComment(
            @PathVariable Long userId,
            @PathVariable Long postId,
            @RequestBody CommentRequest request) {
        return commentService.create(postId, request);
    }
}

7. Коды ответов HTTP

REST использует статус коды выразительно:

@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    
    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody UserRequest req) {
        User user = userService.create(req);
        // 201 Created - ресурс создан
        return ResponseEntity
                .status(HttpStatus.CREATED)
                .header("Location", "/api/v1/users/" + user.getId())
                .body(user);
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        return userService.findById(id)
                .map(ResponseEntity::ok)  // 200 OK
                .orElseGet(() -> 
                    ResponseEntity.notFound().build());  // 404 Not Found
    }
    
    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(
            @PathVariable Long id,
            @RequestBody UserRequest req) {
        try {
            User updated = userService.update(id, req);
            return ResponseEntity.ok(updated);  // 200 OK
        } catch (NotFoundException e) {
            return ResponseEntity.notFound().build();  // 404
        } catch (ValidationException e) {
            return ResponseEntity.badRequest().build();  // 400
        }
    }
    
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.delete(id);
        // 204 No Content - успешное удаление, нет тела
        return ResponseEntity.noContent().build();
    }
}

8. Пример полного REST API

@RestController
@RequestMapping("/api/v1/products")
@CrossOrigin(maxAge = 3600)
public class ProductController {
    private final ProductService productService;

    // GET /api/v1/products
    @GetMapping
    public ResponseEntity<PagedResponse<Product>> listProducts(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size,
            @RequestParam(required = false) String category) {
        
        Page<Product> products = productService.findAll(
            PageRequest.of(page, size),
            category
        );
        
        return ResponseEntity
                .ok()
                .cacheControl(CacheControl.maxAge(300, TimeUnit.SECONDS))
                .body(new PagedResponse<>(products));
    }

    // GET /api/v1/products/123
    @GetMapping("/{id}")
    public ResponseEntity<Product> getProduct(@PathVariable Long id) {
        return productService.findById(id)
                .map(p -> ResponseEntity.ok()
                        .cacheControl(CacheControl.maxAge(600, TimeUnit.SECONDS))
                        .body(p))
                .orElseGet(() -> ResponseEntity.notFound().build());
    }

    // POST /api/v1/products
    @PostMapping
    public ResponseEntity<Product> createProduct(
            @Valid @RequestBody ProductRequest request) {
        Product created = productService.create(request);
        return ResponseEntity
                .created(URI.create("/api/v1/products/" + created.getId()))
                .body(created);
    }

    // PUT /api/v1/products/123
    @PutMapping("/{id}")
    public ResponseEntity<Product> updateProduct(
            @PathVariable Long id,
            @Valid @RequestBody ProductRequest request) {
        Product updated = productService.update(id, request);
        return ResponseEntity.ok(updated);
    }

    // DELETE /api/v1/products/123
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
        productService.delete(id);
        return ResponseEntity.noContent().build();
    }
}

Краткое резюме

Главная цель REST:

  1. ✓ Простота и интуитивность (стандартные HTTP методы)
  2. ✓ Масштабируемость (stateless, кэширование)
  3. ✓ Надёжность (использование правильных HTTP кодов)
  4. ✓ Универсальность (работает с любым клиентом)
  5. ✓ Производительность (кэширование, CDN)

Ключевые принципы:

  • Ресурсоориентированность (существительные, не глаголы)
  • Отсутствие состояния (stateless)
  • Стандартные методы HTTP
  • Правильные коды ответов
  • Кэшируемость
Какая главная цель REST? | PrepBro