Опирается на правила:
R-RATE-1..3,R-FILE-1..5,R-DEP-1..3и X-коды из REST API Style Guide → раздел Rate limiting, файлы, deprecation.
Важно знать
- Rate limit превышен —
429+Retry-After+RateLimit-*headers.- Успешный response включает
RateLimit-Limit,RateLimit-Remaining,RateLimit-Reset.- Файлы через
POSTmultipart/form-dataна вложенный ресурс.- Скачивание через
GETс бинарнымContent-TypeиContent-Disposition.- Deprecation через
deprecated: trueв OpenAPI +Sunset+Deprecation+Link rel=successor-version.- После
Sunsetдаты —410 Goneс указанием альтернативы.- 429 без headers или deprecated без Sunset — запрещены.
Три темы — три разных контекста: rate limiting защищает backend от перегрузки, файлы — отдельный transport (binary, не JSON), deprecation — упорядоченный путь от старой версии к новой.
Rate limiting
R-RATE-1..3:
429 + Retry-After
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/problem+json
{
"type": "urn:problem:order-service:rate-limit-exceeded",
"status": 429,
"title": "Too Many Requests",
"detail": "Превышен лимит запросов. Повторите через 30 секунд.",
"code": "RATE_LIMIT_EXCEEDED"
}
Retry-After: 30 — секунд до сброса лимита. Клиент обязан уважать этот header — не делать retry раньше.
RateLimit-* в success-ответах
HTTP/1.1 200 OK
RateLimit-Limit: 100
RateLimit-Remaining: 57
RateLimit-Reset: 1719849600
{ ... }
RateLimit-Limit— максимум запросов в окне.RateLimit-Remaining— сколько осталось.RateLimit-Reset— Unix timestamp сброса окна.
Клиент видит, сколько ещё может сделать, и заранее замедляет.
OpenAPI
"429":
description: 'Too Many Requests'
headers:
Retry-After:
schema: { type: integer }
description: 'Секунд до сброса лимита'
RateLimit-Limit:
schema: { type: integer }
description: 'Максимум запросов в окне'
RateLimit-Remaining:
schema: { type: integer }
RateLimit-Reset:
schema: { type: integer }
description: 'Unix timestamp сброса окна'
content:
application/problem+json:
schema:
$ref: "#/components/schemas/ProblemDetails"
Запреты
R-RATE-X1: 429 без Retry-After или RateLimit-* — клиент не сможет корректно ретраить.
Загрузка файлов
R-FILE-1..5:
Endpoint
POST /api/v1/documents/{id}/attachments ← attachment к document
POST /api/v1/users/me/avatar ← avatar singleton
Файлы — вложенный ресурс: attachments к document, avatar к user/me.
Request format — multipart/form-data
POST /api/v1/documents/{id}/attachments
Content-Type: multipart/form-data; boundary=----Boundary
------Boundary
Content-Disposition: form-data; name="file"; filename="report.pdf"
Content-Type: application/pdf
<binary data>
------Boundary
Content-Disposition: form-data; name="description"
Отчет за март
------Boundary--
Не Base64 в JSON (раздувает размер на 33%, payload не streamable). Не raw binary в body — теряется метаинформация (filename, content-type).
Ограничения в OpenAPI
requestBody:
content:
multipart/form-data:
schema:
type: object
required: [file]
properties:
file:
type: string
format: binary
description: 'Максимум 10 МБ. Допустимые типы: PDF, PNG, JPG'
description:
type: string
maxLength: 500
Response — 201 + metadata
{
"attachmentId": "550e8400-...",
"fileName": "report.pdf",
"contentType": "application/pdf",
"size": 1048576,
"uploadedAt": "2026-05-26T10:30:00Z"
}
Скачивание — GET + binary
GET /api/v1/documents/{id}/attachments/{id}
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="report.pdf"
Content-Length: 1048576
<binary data>
Content-Disposition: attachment; filename="..." — браузер сохраняет файл с правильным именем. Без — сохраняется как attachment без расширения.
Deprecation
R-DEP-1..3: упорядоченное снятие.
1. Пометить deprecated в OpenAPI
/api/v1/orders/{id}/status:
get:
deprecated: true
summary: 'Получить статус заказа'
description: 'DEPRECATED: используйте GET /api/v2/orders/{id}. Будет удалён после 2026-09-01.'
2. Headers в response
HTTP/1.1 200 OK
Sunset: Sat, 01 Sep 2026 00:00:00 GMT
Deprecation: true
Link: </api/v2/orders/{id}>; rel="successor-version"
Sunset(RFC 8594) — дата отключения.Deprecation: true— boolean флаг (RFC draft).Linkсrel="successor-version"— URL новой версии.
Клиенты, которые следят за headers (production-grade SDK), видят deprecation, начинают миграцию.
3. Процесс вывода из эксплуатации
- Пометить
deprecated: true+ headers. - Уведомить потребителей: changelog, рассылка, Slack.
- Мониторить трафик на устаревший endpoint.
- После
Sunset—410 Gone:
{
"type": "urn:problem:order-service:endpoint-removed",
"status": 410,
"title": "Gone",
"detail": "Эндпоинт удалён. Используйте GET /api/v2/orders/{id}.",
"code": "ENDPOINT_REMOVED"
}
Период между deprecation и Sunset — обычно 6-12 месяцев. Достаточно для миграции крупных клиентов.
Запреты
R-DEP-X1: deprecated: true без Sunset — клиент не знает, когда мигрировать. «Когда-нибудь» = «никогда».
Что запрещено
| Антипаттерн | Правило | Что взамен |
|---|---|---|
429 без Retry-After | R-RATE-X1 | header обязателен |
429 без RateLimit-* | R-RATE-X1 | информировать клиента о лимите |
| RateLimit-* только при превышении | R-RATE-2 | в каждый response |
| File upload через JSON Base64 | R-FILE-2 | multipart/form-data |
File upload без Content-Disposition | R-FILE-2 | filename обязателен |
Скачивание без Content-Disposition | R-FILE-5 | filename для browser |
| Размер файла unlimited в OpenAPI | R-FILE-3 | явные ограничения |
deprecated: true без Sunset | R-DEP-X1 | дата обязательна |
Без Link rel="successor-version" | R-DEP-2 | альтернатива в header |
| Без 410 после Sunset | R-DEP-3 | возвращаем 410 явно |
| Mass deprecation без warning period | R-DEP-3 | минимум 6 месяцев |
Куда дальше
- REST API → Rate limiting, файлы, deprecation (нормативно) — формулировки.
- Ошибки RFC 9457 — 429, 410 формат.
- Версионирование — v1 → v2 → deprecation v1.
- Batch, async, локализация — alternative для long requests.
- JSON и формат ответов — file upload response = ресурс.
- Заголовки и трассировка —
Retry-Afterкак стандартный.