Что входит в набор правил для выполнения REST-запроса?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Правила для выполнения 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.