Перейти к содержанию
HexaTransfer
Вернуться к блогу
Технические погружения

Проектирование API передачи файлов: лучшие практики RESTful

Проектируйте надёжные API передачи файлов по лучшим практикам RESTful. Аутентификация, ограничение запросов, многокомпонентная загрузка и обработка ошибок.

Опубликовано HexaTransfer, сервисом Gaprod — Groupe Dwenola SAS. Руководства рассматривают наш сервис и другие продукты. Об издателе

Грамотно спроектированный REST API передачи файлов открывает пять-шесть ресурсов (сессии, части, расшаривания, загрузки, отзывы), честно использует HTTP-семантику (POST для создания, PUT для идемпотентных чанков, DELETE для отзыва) и перекладывает реальное движение байт на presigned URL хранилища, чтобы ваш сервер никогда не стал узким местом по пропускной способности. Аутентификация через краткосрочные bearer-токены поверх TLS 1.3, ограничения запросов разделяют создание от чтения метаданных, чанкованные загрузки следуют паттерну tus или семантике S3 multipart, а ошибки следуют RFC 7807 Problem Details, чтобы клиенты могли реагировать на них программно.

Моделируйте ресурсы вокруг действий, а не файлов

Типичная ошибка — моделировать API как дерево файлов. Сервисы передачи файлов лучше моделировать как три ресурса:

  • /sessions — активная загрузка, создаётся POST, заполняется через PATCH/PUT чанков
  • /shares — завершённая, адресуемая передача с истечением и бюджетом скачиваний
  • /downloads — краткосрочные подписанные дескрипторы для получения байт

Сессии становятся расшариваниями через действие complete; расшаривания истекают по времени или бюджету скачиваний. Отзывы — это PATCH на расшаривание или DELETE с токеном отзыва. Никаких именных ресурсов для «файла» или «папки» — это детали реализации хранилища, а не ваш публичный контракт.

Такая форма сохраняет поверхность API малой (менее 10 эндпоинтов), чисто отображается на HTTP-кеширование (расшаривания: Cache-Control: private, max-age=60; загрузки: no-store), и позволяет менять бэкенды хранилища без нарушения клиентов.

Паттерны аутентификации и авторизации

Для пользовательских API выпускайте краткосрочные JWT (TTL 15 минут), подписанные EdDSA, и обновляйте через защищённую HttpOnly cookie. Токен — в Authorization: Bearer, никогда в строках запроса, где он попадает в логи.

Для machine-to-machine подпись запросов HMAC-SHA256 превосходит bearer-токены, потому что доказывает владение ключом без его отправки. AWS SigV4 — эталонный дизайн:

Authorization: HEX4-HMAC-SHA256 
  Credential=AKIA.../20261202/eu/transfer/hex4_request,
  SignedHeaders=host;x-hex-date;x-hex-content-sha256,
  Signature=...

Для presigned URL скачивания TTL должен быть в минутах, не в часах. Окно 15 минут балансирует удобство использования против риска пересылки утёкшего URL. Включайте IP-блокировку только если готовы принять поломку пользователей за CGNAT — обычно нет.

Чанкованные загрузки и семантика multipart

Существуют два надёжных протокола для возобновляемых чанкованных загрузок: tus (черновик IETF, заголовки Upload-Offset и Upload-Length) и S3 multipart (PartNumber, UploadId, ETag). Выберите один и придерживайтесь. Изобрести свой протокол кажется соблазнительным, но заканчивается плохо, когда приходит необходимость обрабатывать частичные записи, чанки не по порядку и брошенные сессии.

Паттерн tus в REST-форме:

POST   /sessions              -> 201, Location: /sessions/abc
HEAD   /sessions/abc          -> 200, Upload-Offset: 104857600
PATCH  /sessions/abc          -> 204, body = next chunk
POST   /sessions/abc/complete -> 201, Location: /shares/xyz
DELETE /sessions/abc          -> 204

Границы размеров чанков: минимум 5 МБ для соответствия S3 multipart, максимум 100 МБ для ограничения стоимости повтора, по умолчанию 8 МБ. Отклоняйте чанки, не выровненные с объявленным смещением, через HTTP 409 Conflict с документом Problem, объясняющим ожидаемое смещение.

Ограничение запросов, отражающее реальные злоупотребления

Ограничения запросов должны варьироваться по стоимости эндпоинта:

  • POST /sessions: 20 в час на IP (дорого — выделяет хранилище)
  • PATCH /sessions/:id: 10 000 в час на сессию (дёшево — пишет байты)
  • GET /shares/:id: 1 000 в час на IP (поиск метаданных)
  • GET /shares/:id/download: 100 в час на IP (стоимость исходящего трафика)

Применяйте ограничения на ребре (Cloudflare, Fastly) для базовой защиты, и второй слой в приложении (express-rate-limit, Fastify's @fastify/rate-limit) для контроля всплесков. Возвращайте 429 с Retry-After в секундах. Включайте заголовки ограничений и в успешные ответы:

RateLimit-Limit: 20
RateLimit-Remaining: 17
RateLimit-Reset: 2843

Соответствие черновику IETF RateLimit позволяет корректным клиентам самостоятельно регулировать темп вместо блокировки.

Ответы об ошибках, с которыми клиент может работать

RFC 7807 Problem Details — стандарт. Каждая ошибка возвращает Content-Type: application/problem+json:

{
  "type": "https://api.example.com/errors/chunk-offset-mismatch",
  "title": "Chunk offset mismatch",
  "status": 409,
  "detail": "Server expected offset 5242880, received 4194304.",
  "expected_offset": 5242880,
  "session_id": "abc123"
}

URI type должен быть задокументирован и стабилен — именно по нему клиенты делают сопоставление с образцом. title — общий; detail — конкретный. Пользовательские поля добавляют машиночитаемый контекст. Никогда не утекайте стек-трейсы, пути к файлам или внутренние идентификаторы в ошибках.

Используйте HTTP-коды честно: 400 для некорректного ввода, 401 для отсутствующей аутентификации, 403 для аутентифицированного-но-неавторизованного, 404 только если ресурс никогда не существовал (используйте 410 Gone для истёкших расшариваний), 413 для превышения квоты, 429 для ограничений, 500 для ошибок, 503 для обслуживания.

Согласование контента и стриминг

Эндпоинты загрузки должны принимать application/octet-stream и требовать Content-Length. Отклоняйте multipart/form-data для загрузки чанков — добавляет накладные расходы разбора без пользы. Принимайте Content-Range для tus-стиля частичных записей.

Эндпоинты скачивания должны поддерживать HTTP Range-запросы (RFC 7233) для возобновляемых загрузок:

GET /shares/xyz/blob
Range: bytes=104857600-209715199
-> 206 Partial Content
   Content-Range: bytes 104857600-209715199/2147483648

Именно это позволяет браузерам возобновить загрузку 2 ГБ после проблемы с Wi-Fi. Большинство S3-совместимых хранилищ обслуживают Range-запросы нативно — ваш API просто передаёт или подписывает их.

Версионирование без накопления технического долга

Версионируйте через путь URL (/v1/sessions), не заголовки. Версионирование по пути видно в логах, кешируемо и проще отлаживать, чем Accept: application/vnd.example.v1+json. Поддерживайте v1 минимум 24 месяца после выхода v2. Свободно добавляйте поля (клиенты должны игнорировать неизвестные); никогда не удаляйте и не переименовывайте поля в стабильной версии.

При необходимости ломающих изменений запускайте v1 и v2 параллельно 12 месяцев, добавляйте заголовок Sunset на ответы v1 согласно RFC 8594, и публикуйте руководства по миграции с реальными примерами до и после.

Наблюдаемость и отлаживаемость

Каждый ответ должен включать корреляционный ID (X-Request-ID), отзеркаленный из запроса или сгенерированный. Логируйте ID, IP клиента (хешированный для чувствительных к конфиденциальности), эндпоинт, статус и продолжительность в структурированном JSON. Не логируйте тела запросов — так ключи шифрования попадают в системы мониторинга.

Собирайте метрики по эндпоинту: количество запросов, задержку p50/p95/p99, частоту ошибок, байты входящие, байты исходящие. Оповещайте о всплесках задержки p99 и частоте 5xx выше базового уровня. Трассировки через OpenTelemetry дают поток запросов через API, хранилище и базу данных.

API HexaTransfer следует паттернам выше: короткие ресурсы, загрузки в стиле tus, ошибки Problem Details, presigned загрузки на 15 минут, заголовки RateLimit. Попробуйте на hexatransfer.com — бесплатно, без регистрации, до 50 ГБ.

Документация, соответствующая реальности

Публикуйте спецификацию OpenAPI 3.1 вместе с API и держите её в том же репозитории, что и серверный код, чтобы расхождение схем было вопросом ревью PR, а не сюрпризом в продакшене. Генерируйте хотя бы один SDK (TypeScript или Python) из спецификации и используйте его в своих примерах — баги SDK быстро выявляют баги спецификации. Включайте примеры curl для каждого эндпоинта, быстрый старт, загружающий реальный файл за 20 строк, и страницу с документацией каждого URI type ошибки. API, в который разработчики могут интегрироваться за один день, попадёт в 10 раз больше продуктов, чем тот, что требует недели обратной разработки.

Безопасная отправка больших файлов со сквозным шифрованием

До 50 GiB бесплатно со сквозным шифрованием, без аккаунта и электронной почты. Выберите срок от 1 до 30 дней; по умолчанию 14 дней.

Отправить файл