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

Какие есть принципы у REST приложений?

2.0 Middle🔥 251 комментариев
#API и сетевые протоколы#Архитектура и паттерны

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

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

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

# Принципы REST приложений

Что такое REST

REST (Representational State Transfer) — это архитектурный стиль для проектирования веб-сервисов, разработанный Roy Fielding в 2000 году. REST определяет набор ограничений (constraints), которые делают веб-сервисы **простыми, масштабируемыми и надежными**.

Основные принципы REST

1. Архитектура Client-Server

Клиент и сервер работают независимо друг от друга:

Клиент                    Сервер
  ↓                         ↓
  +------- HTTP запрос -----→
  ←------ HTTP ответ -------+
  ↓                         ↓
УИ, логика клиента    Бизнес-логика, БД

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

  • Они развиваются независимо
  • Сервер не нужно знать про реализацию клиента
  • Клиент не нужно знать про внутреннее устройство сервера
// ✅ REST подход: сервер ничего не знает про клиента
app.get('/api/users/:id', (req, res) => {
  const user = getUserFromDB(req.params.id);
  res.json(user); // Клиент сам решит, что с этим делать
});

// ❌ Не REST: сервер заботится про клиента
app.get('/api/users/:id/render-html', (req, res) => {
  const user = getUserFromDB(req.params.id);
  res.send(`<html><body>${user.name}</body></html>`);
});

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

Каждый запрос содержит все необходимые информации для его обработки. Сервер не хранит контекст между запросами.

// ❌ Плохо: сервер запоминает состояние
let userContext = null;

app.post('/api/login', (req, res) => {
  const user = authenticate(req.body);
  userContext = user; // Запоминаем в памяти
  res.json({ success: true });
});

app.get('/api/profile', (req, res) => {
  // Надеемся, что userContext был установлен ранее
  if (!userContext) res.status(401).send('Not logged in');
  res.json(userContext);
});
// ✅ Хорошо: каждый запрос независим
app.post('/api/login', (req, res) => {
  const user = authenticate(req.body);
  const token = generateToken(user);
  res.json({ token }); // Отправляем token клиенту
});

app.get('/api/profile', (req, res) => {
  const token = req.headers.authorization; // Клиент отправляет token
  const user = getUserFromToken(token);
  res.json(user);
});

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

  • Масштабируемость — каждый запрос может обработать любой сервер
  • Надежность — если сервер упадет, клиент просто переподключится
  • Кэшируемость — каждый запрос идентичен

3. Uniform Interface (Единый интерфейс)

Унифицированный способ взаимодействия между клиентом и сервером.

3.1 Ресурсы как основа

Всё в REST — это ресурсы (существительные, не глаголы):

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

// ❌ Не REST: действия (глаголы)
GET    /api/getUsers
POST   /api/createUser
GET    /api/getUserById?id=123
POST   /api/updateUser
GET    /api/deleteUser?id=123

3.2 HTTP методы имеют значение

// GET - получить ресурс (безопасный, идемпотентный)
GET /api/users/123
// Результат: { id: 123, name: "Alice" }

// POST - создать новый ресурс (не идемпотентный)
POST /api/users
Body: { name: "Bob" }
// Результат: { id: 124, name: "Bob" } (новый)  

// PUT - заменить ресурс целиком (идемпотентный)
PUT /api/users/123
Body: { name: "Alice Updated", email: "alice@example.com" }
// Результат: { id: 123, name: "Alice Updated", email: "alice@example.com" }

// PATCH - частичное обновление (не всегда идемпотентный)
PATCH /api/users/123
Body: { name: "Alice New" }
// Результат: { id: 123, name: "Alice New", email: остается старый }

// DELETE - удалить ресурс (идемпотентный)
DELETE /api/users/123
// Результат: 204 No Content

Таблица методов:

МетодБезопасныйИдемпотентныйКэшируетсяИспользование
GETДаДаДаПолучить данные
POSTНетНетНетСоздать ресурс
PUTНетДаНетПолное обновление
PATCHНетНетНетЧастичное обновление
DELETEНетДаНетУдалить ресурс
// Пример идемпотентности
PUT /api/users/123 { name: "Alice" } // 1-й запрос: 200 OK
PUT /api/users/123 { name: "Alice" } // 2-й запрос: 200 OK (результат одинаков)
PUT /api/users/123 { name: "Alice" } // 3-й запрос: 200 OK

// vs POST (не идемпотентный)
POST /api/users { name: "Alice" } // 1-й запрос: 201 Created (создан пользователь 1)
POST /api/users { name: "Alice" } // 2-й запрос: 201 Created (создан пользователь 2)
POST /api/users { name: "Alice" } // 3-й запрос: 201 Created (создан пользователь 3)

3.3 Правильные HTTP статус коды

// 2xx - Успех
app.get('/api/users', (req, res) => {
  res.status(200).json(users); // OK
});

app.post('/api/users', (req, res) => {
  const user = createUser(req.body);
  res.status(201).json(user); // Created
});

app.delete('/api/users/123', (req, res) => {
  deleteUser(123);
  res.status(204).send(); // No Content
});

// 4xx - Ошибка клиента
app.get('/api/users/999', (req, res) => {
  if (!userExists(999)) {
    res.status(404).json({ error: 'User not found' }); // Not Found
  }
});

app.post('/api/users', (req, res) => {
  if (!req.body.name) {
    res.status(400).json({ error: 'Name is required' }); // Bad Request
  }
});

app.get('/api/admin', (req, res) => {
  if (!hasPermission(req.user)) {
    res.status(403).json({ error: 'Forbidden' }); // Forbidden
  }
});

// 5xx - Ошибка сервера
app.get('/api/data', (req, res) => {
  try {
    const data = fetchFromDB();
    res.json(data);
  } catch (error) {
    res.status(500).json({ error: 'Internal Server Error' });
  }
});

4. Cacheability (Кэшируемость)

Ответы должны явно определять, кэшируемы ли они:

app.get('/api/users/:id', (req, res) => {
  const user = getUser(req.params.id);
  
  // Кэшируется на 1 час
  res.set('Cache-Control', 'public, max-age=3600');
  res.json(user);
});

app.post('/api/users', (req, res) => {
  const user = createUser(req.body);
  
  // Не кэшируется
  res.set('Cache-Control', 'no-cache, no-store');
  res.status(201).json(user);
});

app.get('/api/profile', (req, res) => {
  // Приватные данные: кэшируется только у клиента
  res.set('Cache-Control', 'private, max-age=1800');
  res.json(getUserProfile(req.user.id));
});

5. Layered System (Слоистая архитектура)

Сервер может состоять из нескольких слоев, и клиент не знает, с каким он общается:

Клиент
   ↓
 Load Balancer (распределение нагрузки)
   ↓
API Gateway (аутентификация, rate limiting)
   ↓
Microservice 1, Microservice 2, Microservice 3
   ↓
Database
   ↓
Cache
// ✅ Клиент не знает про эти детали
app.get('/api/users/123', async (req, res) => {
  // Может быть кэш
  let user = cache.get('user:123');
  if (!user) {
    // Или БД
    user = await database.users.findById(123);
    cache.set('user:123', user);
  }
  res.json(user);
});

6. Code on Demand (опционально)

Сервер может отправить код, который клиент выполнит:

// ✅ Пример: сервер отправляет JavaScript
app.get('/api/report', (req, res) => {
  const data = getReportData();
  const code = `
    const data = ${JSON.stringify(data)};
    console.log('Processed:', data.map(d => d * 2));
  `;
  res.json({ code, data });
});

Это редко используется в REST, обычно в Single Page Applications (SPA).

Лучшие практики REST

// 1. Версионирование API
app.get('/api/v1/users');
app.get('/api/v2/users');

// 2. Pagination
app.get('/api/users?page=1&limit=20');

// 3. Filtering
app.get('/api/users?role=admin&status=active');

// 4. Sorting
app.get('/api/users?sort=name&order=asc');

// 5. Content negotiation
app.get('/api/users', (req, res) => {
  if (req.accepts('json')) res.json(users);
  else if (req.accepts('xml')) res.send(usersToXml(users));
});

// 6. Вложенные ресурсы
GET /api/users/123/posts           // Посты пользователя 123
GET /api/users/123/posts/456       // Конкретный пост
POST /api/users/123/posts          // Создать пост для пользователя

// 7. Правильные error responses
app.get('/api/users/invalid', (req, res) => {
  res.status(400).json({
    status: 'error',
    code: 'INVALID_USER_ID',
    message: 'User ID must be a number',
    timestamp: new Date().toISOString()
  });
});

Что НЕ REST

// ❌ RPC (Remote Procedure Call)
POST /api/getUser { id: 123 }
POST /api/deleteUser { id: 123 }

// ❌ SOAP (Simple Object Access Protocol)
// Использует XML и очень сложен

// ❌ GraphQL
// Другой подход к API (но он может быть лучше REST в некоторых случаях)

Вывод

Принципы REST обеспечивают:

  1. Масштабируемость — сервер может расширяться
  2. Надежность — statelessness позволяет отказоустойчивость
  3. Простота — легко понять и использовать
  4. Кэшируемость — улучшает производительность
  5. Стандартизация — все разработчики понимают REST

REST остается золотым стандартом для веб-API в 2025 году, хотя есть альтернативы (GraphQL, gRPC).

Какие есть принципы у REST приложений? | PrepBro