Опирается на правила: 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.
  • Файлы через POST multipart/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. Процесс вывода из эксплуатации

  1. Пометить deprecated: true + headers.
  2. Уведомить потребителей: changelog, рассылка, Slack.
  3. Мониторить трафик на устаревший endpoint.
  4. После Sunset410 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-AfterR-RATE-X1header обязателен
429 без RateLimit-*R-RATE-X1информировать клиента о лимите
RateLimit-* только при превышенииR-RATE-2в каждый response
File upload через JSON Base64R-FILE-2multipart/form-data
File upload без Content-DispositionR-FILE-2filename обязателен
Скачивание без Content-DispositionR-FILE-5filename для browser
Размер файла unlimited в OpenAPIR-FILE-3явные ограничения
deprecated: true без SunsetR-DEP-X1дата обязательна
Без Link rel="successor-version"R-DEP-2альтернатива в header
Без 410 после SunsetR-DEP-3возвращаем 410 явно
Mass deprecation без warning periodR-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 как стандартный.