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

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

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

Грамотно спроектированный 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 — бесплатно, без регистрации, до 10 ГБ.

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

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

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

Передавайте файлы до 10 ГБ бесплатно со сквозным шифрованием. Регистрация не требуется. Ваши файлы шифруются в браузере перед загрузкой — никто другой не может их прочитать.

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