Projektowanie API przesyłania plików: praktyki RESTful
Projektuj solidne API do przesyłania plików zgodnie z najlepszymi praktykami RESTful. Uwierzytelnianie, limitowanie żądań, przesyłanie multipart i obsługa błędów.
Dobrze zaprojektowane REST API do transferu plików to jeden z elementów, który CSIRT NASK wskazuje w wytycznych bezpiecznego rozwoju oprogramowania — obejmuje uwierzytelnianie, ograniczanie liczby żądań i prawidłową obsługę błędów. Dobrze zaprojektowane API eksponuje pięć lub sześć zasobów (sesje, części, udostępnienia, pobierania, odwołania), używa semantyki HTTP uczciwie (POST do tworzenia, PUT dla idempotentnych fragmentów, DELETE do odwoływania) i przesuwa faktyczny ruch bajtów do presignowanych URL-i storage, żeby twój serwer nigdy nie stał się wąskim gardłem przepustowości. Uwierzytelnianie używa krótkotrwałych tokenów bearer przez TLS 1.3, limity szybkości różnicują tworzenie od odczytów metadanych, fragmentowane uploady podążają za wzorcem wznawiania tus.io lub semantyką multipart S3, a błędy podążają za RFC 7807 Problem Details, żeby klienty mogły na nich programowo działać.
Kształtuj model zasobów wokół akcji, nie plików
Typowy błąd to modelowanie API jako drzewa plików. Usługi transferu plików są lepiej modelowane jako trzy zasoby:
/sessions— trwający upload, tworzony przez POST, uzupełniany przez PUTy fragmentów/shares— ukończony, adresowalny transfer z terminem ważności i budżetem pobrań/downloads— krótkotrwałe, podpisane uchwyty dostępu do pobierania bajtów
Sesje stają się udostępnieniami przez akcję complete; udostępnienia wygasają przez czas lub budżet pobrań. Odwołania to PATCH na udostępnieniu lub DELETE z tokenem odwołania. Bez rzeczowników dla "pliku" czy "folderu" — to szczegóły implementacji storage, nie twój publiczny kontrakt.
Ten kształt utrzymuje powierzchnię API małą (poniżej 10 endpointów), mapuje czysto do buforowania HTTP (udostępnienia są Cache-Control: private, max-age=60; pobierania są no-store) i pozwala na wymianę backendów storage bez łamania klientów.
Wzorce uwierzytelniania i autoryzacji
Dla API skierowanych do użytkownika, wystawiaj krótkotrwałe JWT (TTL 15 minut) podpisane EdDSA i odświeżaj przez bezpieczne ciasteczko HttpOnly. Umieść token w Authorization: Bearer, nigdy w ciągach zapytania, gdzie trafia do logów.
Dla machine-to-machine, podpisywanie żądań HMAC-SHA256 bije tokeny bearer, bo dowodzi posiadania klucza bez jego wysyłania. AWS SigV4 to projekt referencyjny:
Authorization: HEX4-HMAC-SHA256
Credential=AKIA.../20261202/eu/transfer/hex4_request,
SignedHeaders=host;x-hex-date;x-hex-content-sha256,
Signature=...
Dla presignowanych URL-i pobierania TTL powinny być minuty, nie godziny. Okno 15 minut balansuje użyteczność względem ryzyka wyciekniętego URL-a przekazywanego dalej.
Fragmentowane uploady i semantyka multipart
Istnieją dwa wiarygodne protokoły dla wznawianych fragmentowanych uploadów: tus (szkic IETF, nagłówki Upload-Offset i Upload-Length) i S3 multipart (PartNumber, UploadId, ETag). Wybierz jeden i trzymaj się go. Wynajdowanie własnego protokołu wygląda kuszącą i kończy się źle gdy zdasz sobie sprawę, że musisz obsługiwać częściowe zapisy, fragmenty poza kolejnością i porzucone sesje.
Wzorzec tus w formie REST:
POST /sessions -> 201, Location: /sessions/abc
HEAD /sessions/abc -> 200, Upload-Offset: 104857600
PATCH /sessions/abc -> 204, body = następny fragment
POST /sessions/abc/complete -> 201, Location: /shares/xyz
DELETE /sessions/abc -> 204
Granice rozmiaru fragmentu: minimum 5 MB żeby pasować do multipart S3, maksimum 100 MB żeby koszt ponowień był ograniczony, domyślnie 8 MB. Odrzucaj fragmenty, które nie wyrównują z ogłoszonym offsetem przez HTTP 409 Conflict plus dokument Problem wyjaśniający oczekiwany offset.
Limitowanie szybkości odzwierciedlające rzeczywiste nadużycia
Limity szybkości muszą różnić się kosztem endpointu:
POST /sessions: 20 na IP na godzinę (drogie — alokuje storage)PATCH /sessions/:id: 10 000 na godzinę na sesję (tanie — zapisuje bajty)GET /shares/:id: 1000 na IP na godzinę (lookup metadanych)GET /shares/:id/download: 100 na IP na godzinę (koszt ruchu wychodzącego)
Stosuj limity na krawędzi (Cloudflare, Fastly) dla ochrony podłogowej i drugą warstwę w aplikacji (express-rate-limit, @fastify/rate-limit Fastify) dla kontroli burst. Zwracaj 429 z Retry-After w sekundach. Uwzględniaj nagłówki limitu szybkości w udanych odpowiedziach też:
RateLimit-Limit: 20
RateLimit-Remaining: 17
RateLimit-Reset: 2843
Podążanie za szkicem IETF RateLimit pozwala dobrze zachowującym się klientom na samodzielne temper zamiast bycia blokowanymi.
Odpowiedzi błędów, na których klienty mogą działać
RFC 7807 Problem Details to standard. Każdy błąd zwraca 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 powinno być udokumentowane i stabilne — to na czym klienty dopasowują wzorce. title jest ogólny; detail jest konkretny. Niestandardowe pola dodają kontekst możliwy do działania przez maszynę. Nigdy nie wyciekaj stack traces, ścieżek plików ani wewnętrznych ID w błędach.
Negocjacja treści i strumieniowanie
Endpointy uploadu powinny akceptować application/octet-stream i wymagać Content-Length. Odrzucaj multipart/form-data dla fragmentowych uploadów — dodaje narzut parsowania i nikomu nie pomaga. Akceptuj Content-Range dla częściowych zapisów w stylu tus.
Endpointy pobierania powinny obsługiwać żądania HTTP Range (RFC 7233) dla wznawianych pobierań:
GET /shares/xyz/blob
Range: bytes=104857600-209715199
-> 206 Partial Content
Content-Range: bytes 104857600-209715199/2147483648
To co pozwala przeglądarkom wznawiać pobieranie 2 GB po zerwaniu Wi-Fi. Większość magazynów kompatybilnych z S3 obsługuje żądania Range natywnie — twoje API po prostu musi je przekazywać lub presignować.
Wersjonowanie bez gromadzenia długu
Wersjonuj przez ścieżkę URL (/v1/sessions) nie nagłówki. Wersjonowanie ścieżki jest widoczne w logach, możliwe do buforowania i łatwiejsze do debugowania. Utrzymuj v1 obsługiwaną przez co najmniej 24 miesiące po wdrożeniu v2. Dodawaj pola swobodnie (klienty muszą ignorować nieznane pola); nigdy nie usuwaj ani nie zmieniaj nazw pól w stabilnej wersji.
Gdy niezbędne są zmiany łamiące, uruchamiaj v1 i v2 równolegle przez 12 miesięcy, wyświetlaj nagłówek Sunset w odpowiedziach v1 zgodnie z RFC 8594 i publikuj przewodniki migracji.
Obserwowalność i debugowalność
Każda odpowiedź powinna zawierać ID korelacji (X-Request-ID) powtórzone z żądania lub wygenerowane. Loguj ID, IP klienta (zahashowane jeśli wrażliwe na prywatność), endpoint, status i czas trwania w ustrukturyzowanym JSON. Nie loguj ciał żądań — tak klucze szyfrowania trafiają do Datadoga.
Emituj metryki per endpoint: liczba żądań, opóźnienia p50/p95/p99, wskaźnik błędów, bajty wchodzące, bajty wychodzące. Śledź ślady przez OpenTelemetry dla przepływu żądań między API, storage i bazą danych.
HexaTransfer API podąża za powyższymi wzorcami — krótkie zasoby, fragmentowane uploady w stylu tus, błędy Problem Details, 15-minutowe presignowane pobierania, nagłówki RateLimit. Wypróbuj na https://hexatransfer.com — za darmo, bez konta, maks. 10 GB.
Dokumentacja zgodna z rzeczywistością
Publikuj specyfikację OpenAPI 3.1 razem z API i trzymaj ją w tym samym repo co kod serwera, żeby dryft schematu był problemem recenzji PR, a nie niespodzianką produkcyjną. Wygeneruj co najmniej jeden SDK (TypeScript lub Python) ze specyfikacji i używaj go we własnych przykładach — błędy SDK szybko ujawniają błędy specyfikacji. Uwzględnij przykłady curl dla każdego endpointu, quickstart uploadujący prawdziwy plik w poniżej 20 liniach i stronę dokumentującą każdy URI type błędu.
Wysyłaj duże pliki bezpiecznie z szyfrowaniem end-to-end
Przesyłaj pliki do 10 GB za darmo z szyfrowaniem end-to-end. Bez rejestracji. Twoje pliki są szyfrowane w przeglądarce przed przesłaniem — nikt inny nie może ich odczytać.
Wyślij plik