Проектирование 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 дней.
Отправить файл