Комментарии (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:
- ✓ Простота и интуитивность (стандартные HTTP методы)
- ✓ Масштабируемость (stateless, кэширование)
- ✓ Надёжность (использование правильных HTTP кодов)
- ✓ Универсальность (работает с любым клиентом)
- ✓ Производительность (кэширование, CDN)
Ключевые принципы:
- Ресурсоориентированность (существительные, не глаголы)
- Отсутствие состояния (stateless)
- Стандартные методы HTTP
- Правильные коды ответов
- Кэшируемость