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

Что входит в набор правил для выполнения REST-запроса?

1.0 Junior🔥 81 комментариев
#Браузер и сетевые технологии

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

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

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

Правила для выполнения REST-запроса

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

Основные правила REST

1. Использование HTTP методов (verbs)

Каждый HTTP метод имеет определённое назначение:

// GET — получить данные (безопасный, идемпотентный)
fetch('/api/v1/users', { method: 'GET' })
fetch('/api/v1/users/123', { method: 'GET' })

// POST — создать новый ресурс
fetch('/api/v1/users', {
  method: 'POST',
  body: JSON.stringify({ name: 'John', email: 'john@example.com' })
})

// PUT — полностью заменить ресурс
fetch('/api/v1/users/123', {
  method: 'PUT',
  body: JSON.stringify({ name: 'Jane', email: 'jane@example.com' })
})

// PATCH — частично обновить ресурс
fetch('/api/v1/users/123', {
  method: 'PATCH',
  body: JSON.stringify({ name: 'Jane' }) // только имя
})

// DELETE — удалить ресурс
fetch('/api/v1/users/123', { method: 'DELETE' })

Правило:

  • GET и DELETE должны быть идемпотентными (повторный вызов = тот же результат)
  • POST не идемпотентный (создаёт новый ресурс каждый раз)
  • PUT и PATCH обычно идемпотентные

2. Ресурсы — существительные, не глаголы

// ❌ Неправильно: глаголы в URL
fetch('/api/v1/get-users')
fetch('/api/v1/create-user')
fetch('/api/v1/delete-user')
fetch('/api/v1/update-user')

// ✅ Правильно: существительные
fetch('/api/v1/users', { method: 'GET' })       // получить
fetch('/api/v1/users', { method: 'POST' })      // создать
fetch('/api/v1/users/123', { method: 'DELETE' }) // удалить
fetch('/api/v1/users/123', { method: 'PATCH' })  // обновить

3. Структура URL

URL должен ясно представлять иерархию ресурсов:

// ✅ Правильная структура
/api/v1/users                  // коллекция users
/api/v1/users/123              // конкретный user (id=123)
/api/v1/users/123/posts        // посты конкретного user
/api/v1/users/123/posts/456    // конкретный пост конкретного user
/api/v1/posts/456/comments     // комментарии к посту
/api/v1/posts/456/comments/789 // конкретный комментарий

// Или с UUID вместо ID
/api/v1/users/9f46a617-a915-40f1-af22-11ac997df0e3
/api/v1/users/john-doe         // slug

// Без trailing slash
/api/v1/users          ✅
/api/v1/users/         ❌

4. Query Parameters для фильтрации

Query параметры используются для:

  • Фильтрации
  • Сортировки
  • Пагинации
  • Поиска
// Фильтрация
fetch('/api/v1/posts?status=published&author_id=123')
fetch('/api/v1/users?role=admin&active=true')

// Пагинация
fetch('/api/v1/posts?page=2&limit=10')
fetch('/api/v1/posts?offset=20&limit=10')

// Сортировка
fetch('/api/v1/posts?sort=created_at&order=desc')
fetch('/api/v1/posts?sort=-created_at')  // минус = по убыванию

// Поиск
fetch('/api/v1/posts?search=javascript')

// Комбинация
fetch('/api/v1/posts?status=published&sort=-created_at&page=1&limit=20')

// Или через URLSearchParams
const params = new URLSearchParams({
  status: 'published',
  sort: '-created_at',
  page: 1,
  limit: 20
})
fetch(`/api/v1/posts?${params}`)

5. Content-Type Header

Опредляет формат данных в теле запроса:

// JSON
fetch('/api/v1/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ name: 'John' })
})

// Form data
const formData = new FormData()
formData.append('name', 'John')
formData.append('avatar', fileInput.files[0])
fetch('/api/v1/users', {
  method: 'POST',
  body: formData
  // Content-Type автоматически: multipart/form-data
})

// x-www-form-urlencoded
fetch('/api/v1/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  body: 'email=john@example.com&password=secret'
})

6. Accept Header

Опредляет ожидаемый формат ответа:

// Ожидаю JSON
fetch('/api/v1/users', {
  headers: {
    'Accept': 'application/json'
  }
})

// Ожидаю XML
fetch('/api/v1/users', {
  headers: {
    'Accept': 'application/xml'
  }
})

// Несколько форматов с приоритетом
fetch('/api/v1/users', {
  headers: {
    'Accept': 'application/json; q=0.9, application/xml; q=0.8'
  }
})

7. Аутентификация

Как передавать credentials:

// Bearer Token (OAuth 2.0)
fetch('/api/v1/me', {
  headers: {
    'Authorization': 'Bearer eyJhbGciOiJIUzI1NiI...'
  }
})

// Basic Auth
fetch('/api/v1/users', {
  headers: {
    'Authorization': 'Basic ' + btoa('username:password')
  }
})

// API Key
fetch('/api/v1/users', {
  headers: {
    'X-API-Key': 'secret-key-123'
  }
})

// Cookies (если credentials: 'include')
fetch('/api/v1/me', {
  credentials: 'include' // отправить cookies
})

8. HTTP Status Codes

Сервер должен возвращать правильные коды состояния:

// 2xx — успех
200 OK              // успешный GET, PUT, PATCH
201 Created         // успешный POST с созданием ресурса
204 No Content      // успешный DELETE

// 3xx — редирект
301 Moved Permanently
302 Found
304 Not Modified    // ресурс не изменился

// 4xx — ошибка клиента
400 Bad Request     // некорректный запрос
401 Unauthorized    // нужна аутентификация
403 Forbidden       // нет доступа
404 Not Found       // ресурс не найден
409 Conflict        // конфликт (например, duplicate email)
422 Unprocessable Entity // данные не прошли валидацию

// 5xx — ошибка сервера
500 Internal Server Error
502 Bad Gateway
503 Service Unavailable

Обработка ошибок:

const handleResponse = async (response) => {
  if (response.ok) {
    return response.json()
  }

  // Обработать ошибку
  if (response.status === 401) {
    // Перенаправить на логин
    redirectToLogin()
  } else if (response.status === 404) {
    throw new Error('Ресурс не найден')
  } else if (response.status === 422) {
    const { errors } = await response.json()
    throw new ValidationError(errors)
  } else {
    throw new Error(`HTTP Error: ${response.status}`)
  }
}

fetch('/api/v1/users').then(handleResponse)

9. Request Body (для POST, PUT, PATCH)

// POST — создать
const user = {
  name: 'John Doe',
  email: 'john@example.com',
  age: 30
}

fetch('/api/v1/users', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(user)
})

// PUT — полностью заменить
// Обязательно передай ВСЕ поля
fetch('/api/v1/users/123', {
  method: 'PUT',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'Jane Doe',
    email: 'jane@example.com',
    age: 28
  })
})

// PATCH — частичное обновление
// Передай только изменённые поля
fetch('/api/v1/users/123', {
  method: 'PATCH',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'Jane Doe'
  })
})

10. Response Format

Ответ должен быть консистентным:

// Успешный ответ (GET)
{
  "id": 123,
  "name": "John Doe",
  "email": "john@example.com",
  "created_at": "2024-01-15T10:30:00Z"
}

// Успешный ответ со списком
{
  "data": [
    { "id": 1, "name": "John" },
    { "id": 2, "name": "Jane" }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 100
  }
}

// Ошибка
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": {
      "email": "Invalid email format",
      "age": "Must be at least 18"
    }
  }
}

11. CORS (Cross-Origin Resource Sharing)

Для запросов с другого домена нужны правильные headers:

// Браузер отправляет CORS headers
fetch('https://api.example.com/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Origin': 'https://myapp.com'  // браузер добавляет
  },
  body: JSON.stringify({ name: 'John' })
})

// Сервер должен вернуть CORS headers
// Access-Control-Allow-Origin: https://myapp.com
// Access-Control-Allow-Methods: GET, POST, PUT, DELETE
// Access-Control-Allow-Headers: Content-Type
// Access-Control-Allow-Credentials: true

12. Idempotency Key (для безопасности)

Для критичных операций используй идемпотентность:

// Если запрос повторится, сервер вернёт тот же результат
const idempotencyKey = crypto.randomUUID()

fetch('/api/v1/payments', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Idempotency-Key': idempotencyKey
  },
  body: JSON.stringify({
    amount: 100,
    currency: 'USD'
  })
})

// Если сеть упала и переотправили — payment произойдёт только один раз

Чеклист для REST запроса

✅ Используешь правильный HTTP метод (GET/POST/PUT/PATCH/DELETE)
✅ URL ресурса во множественном числе (/users, не /user)
✅ Нет глаголов в URL (/users, не /get-users)
✅ Query параметры для фильтрации, пагинации, поиска
✅ Content-Type заголовок определён
✅ Accept заголовок определён (если нужно)
✅ Аутентификация (Authorization header)
✅ Body формат правильный (JSON)
✅ Обработка HTTP status codes
✅ Обработка ошибок
✅ CORS headers если cross-origin
✅ Версионирование API (/api/v1/)
✅ No trailing slashes

Примеры правильно построенного REST API

// Полный пример: CRUD для Posts

// CREATE
const createPost = (postData) => 
  fetch('/api/v1/posts', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(postData)
  })

// READ
const getPostById = (id) => 
  fetch(`/api/v1/posts/${id}`)

const getAllPosts = ({ page = 1, limit = 10, sort = '-created_at' } = {}) => 
  fetch(`/api/v1/posts?page=${page}&limit=${limit}&sort=${sort}`)

// UPDATE
const updatePost = (id, postData) => 
  fetch(`/api/v1/posts/${id}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(postData)
  })

// DELETE
const deletePost = (id) => 
  fetch(`/api/v1/posts/${id}`, { method: 'DELETE' })

Основная идея: REST — это стиль, где ресурсы представляются URI, действия — HTTP методы, и коммуникация через стандартные HTTP статусы и headers.

Что входит в набор правил для выполнения REST-запроса? | PrepBro