format: binary, multipart/form-data und Content-Disposition richtig einsetzen
OpenAPI-Spezifikationen für Datei-Operationen enthalten häufig Fehler: falsch verwendetes format, fehlende encoding-Objekte und undokumentierte Response-Header für Downloads. Dieser Artikel zeigt, wie Uploads und Downloads in OpenAPI 3.1 präzise modelliert werden — so dass Client-Generatoren korrekten Code produzieren und Integratoren keine Überraschungen erleben.
Inhaltsverzeichnis
- 1. OpenAPI und binäre Daten: Grundlagen
- 2. format: binary vs. format: byte — der kritische Unterschied
- 3. multipart/form-data vollständig modellieren
- 4. Das encoding-Objekt für gemischte Content-Types
- 5. Download-Endpoints korrekt beschreiben
- 6. Content-Disposition und Dateinamen im Response-Header
- 7. Mehrere Dateien in einem Request beschreiben
- 8. Client-Generierung mit format: binary
- 9. Upload-Modellierungsansätze im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. OpenAPI und binäre Daten: Grundlagen
OpenAPI beschreibt HTTP-Schnittstellen, und HTTP kann beliebige Binärdaten transportieren. Die Spezifikation muss daher einen Weg bieten, zwischen einfachen Textwerten, base64-kodierten Binärdaten und rohen Dateiinhalten zu unterscheiden. In OpenAPI 3.x wird das über das format-Feld im Schema gelöst: string ist der Basistyp, format: binary und format: byte sind zwei unterschiedliche Interpretationen. Wer den Unterschied nicht kennt, dokumentiert Datei-Endpoints falsch — mit der Folge, dass generierte Clients unbrauchbaren Code produzieren und manuelle Integratoren die falsche Kodierung wählen.
Der zweite Grundlagenaspekt: OpenAPI beschreibt Datei-Uploads und Downloads asymmetrisch. Beim Upload beschreibt man den requestBody mit dem korrekten Content-Type und Schema. Beim Download beschreibt man die responses-Einträge mit dem Content-Type der zurückgegebenen Datei und dem Schema der Binärdaten. Beide Seiten erfordern unterschiedliche YAML-Strukturen, und genau hier entstehen die meisten Dokumentationsfehler in der Praxis: Entwickler kopieren Upload-Strukturen für Downloads oder verwechseln, wo das schema-Objekt platziert werden muss.
2. format: binary vs. format: byte — der kritische Unterschied
format: byte beschreibt base64-kodierte Binärdaten, die als normaler JSON-String übertragen werden. Das ist sinnvoll, wenn eine kleine Binärdatei (z.B. ein kleines Bild oder ein Zertifikat) als Teil eines JSON-Objekts übertragen werden soll. Der Client kodiert die Binärdaten in Base64, bettet den String in das JSON ein und sendet Content-Type: application/json. Die Datei wird beim Empfang dekodiert. Diese Methode erhöht die Payload-Größe um ca. 33% durch die Base64-Kodierung.
format: binary hingegen beschreibt rohe Binärdaten, die nicht JSON-enkodiert werden. Dieser Format-Hinweis wird ausschließlich im Kontext von multipart/form-data oder application/octet-stream verwendet. Client-Generatoren interpretieren format: binary als Signal, die Datei als Datei-Part zu senden, nicht als String. Wer format: binary innerhalb eines normalen JSON-Schemas ohne multipart-Context verwendet, erzeugt eine inkonsistente Spezifikation: JSON kann keine Binärdaten direkt enthalten, und Tools werden uneinheitlich damit umgehen.
# openapi/schemas/file-schemas.yaml
components:
schemas:
# WRONG: binary inside regular JSON request — JSON cannot contain raw binary
BadUploadRequest:
type: object
properties:
file:
type: string
format: binary # incorrect: binary outside multipart context
name:
type: string
# CORRECT: base64 encoding for embedding binary in JSON
EmbeddedFileRequest:
type: object
required: [fileData, filename]
properties:
fileData:
type: string
format: byte # base64-encoded, suitable inside JSON
description: Base64-encoded file content (max 1 MB due to size overhead)
filename:
type: string
description: Original filename for storage
mimeType:
type: string
description: MIME type of the encoded file
# CORRECT: binary used in multipart/form-data context (see requestBody)
DocumentUploadResponse:
type: object
properties:
id:
type: string
format: uuid
url:
type: string
format: uri
size:
type: integer
description: File size in bytes
3. multipart/form-data vollständig modellieren
Ein vollständig modellierter multipart/form-data-Endpoint in OpenAPI 3.1 verwendet das requestBody-Objekt mit einem content-Schlüssel für den MIME-Type multipart/form-data. Das schema-Objekt darunter beschreibt alle Formularfelder als normales JSON-Schema — Textfelder als type: string, numerische Felder als type: integer, und Datei-Felder als type: string, format: binary. Das required-Array auf Schema-Ebene gibt an, welche Felder zwingend vorhanden sein müssen. Diese Struktur ist der einzige Weg, Upload-Endpoints so zu beschreiben, dass Swagger UI ein funktionsfähiges Testformular generiert und OpenAPI-Client-Generatoren korrekte Multipart-Requests produzieren.
Ein häufiger Fehler ist das Weglassen des required-Arrays im Schema — ohne es sind alle Felder optional, und Integratoren erfahren erst zur Laufzeit, welche Felder tatsächlich Pflicht sind. Ein zweiter Fehler: Wenn die API mehrere verschiedene Datei-Typen akzeptiert (z.B. Bild oder PDF, aber nicht beides gleichzeitig), wird das über oneOf auf Schema-Ebene modelliert, nicht durch separate Endpoints. OpenAPI 3.1 hat hier volle JSON-Schema-Unterstützung und erlaubt oneOf, anyOf und discriminator auch in requestBody-Schemata.
# openapi/paths/upload-complete.yaml
/api/v1/documents:
post:
operationId: uploadDocument
summary: Upload document with metadata
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required:
- file
- title
properties:
file:
type: string
format: binary
description: Document file (PDF or image, max 20 MB)
title:
type: string
minLength: 3
maxLength: 200
description: Human-readable document title
tags:
type: array
items:
type: string
maxItems: 10
description: Optional classification tags
expiresAt:
type: string
format: date
description: Optional expiry date (ISO 8601)
encoding:
file:
contentType: application/pdf, image/jpeg, image/png, image/webp
responses:
'201':
description: Document created
headers:
Location:
schema:
type: string
format: uri
description: URL of the newly created document resource
content:
application/json:
schema:
$ref: '#/components/schemas/Document'
4. Das encoding-Objekt für gemischte Content-Types
Das encoding-Objekt im requestBody erlaubt es, für einzelne Felder eines Multipart-Requests den Content-Type zu spezifizieren. Das ist besonders wichtig für das Datei-Feld: Wenn die API nur PDFs akzeptiert, sollte encoding.file.contentType: application/pdf stehen, damit Tools und Integratoren wissen, dass andere Typen abgelehnt werden. Wenn zusätzlich Header für einzelne Parts gesetzt werden sollen — etwa ein spezifischer Zeichensatz für ein Textfeld — können diese im encoding.field.headers-Objekt definiert werden.
Ein fortgeschrittenes Szenario: Ein Multipart-Request enthält einen Binär-Part und einen JSON-Part. Ohne explizites encoding-Objekt ist unklar, wie der JSON-Part kodiert wird. Mit encoding.metadata.contentType: application/json wird definiert, dass dieses Feld im Multipart-Body selbst als JSON-String kodiert ist. Der empfangende Server muss diesen Part dann als JSON parsen. Dieses Muster ermöglicht strukturierte Metadaten zusammen mit Binärdaten in einem einzigen Netzwerk-Roundtrip — ohne die Metadaten als separate Formularfelder zu flachen Strings serialisieren zu müssen.
5. Download-Endpoints korrekt beschreiben
Download-Endpoints geben binäre Daten zurück, was in OpenAPI über die responses-Sektion beschrieben wird. Der korrekte Content-Type für generische Datei-Downloads ist application/octet-stream. Wenn die API immer einen spezifischen Typ zurückgibt (z.B. immer PDF), sollte application/pdf verwendet werden — das ist präziser und erlaubt dem Client, den korrekten Decoder zu wählen. Das Schema des Response-Bodys ist in beiden Fällen {type: string, format: binary}. Diese Kombination signalisiert OpenAPI-Tools, dass der Response-Body rohe Binärdaten enthält.
Eine wichtige Ergänzung für Download-Endpoints: Wenn die API sowohl JSON-Metadaten als auch den Binärinhalt liefern kann (z.B. über Accept-Header), müssen beide Content-Types unter content beschrieben werden. content: application/json: schema: DocumentMetadata für die Metadaten-Variante und content: application/pdf: schema: {type: string, format: binary} für die Binär-Variante. Swagger UI zeigt dann beide Varianten an und Integratoren wissen, dass sie über den Accept-Header zwischen JSON und Binär wählen können.
6. Content-Disposition und Dateinamen im Response-Header
Der Content-Disposition-Header steuert, ob der Browser eine Datei inline anzeigt oder als Download speichert. Content-Disposition: attachment; filename="bericht-2026-05.pdf" löst einen Download-Dialog aus. Content-Disposition: inline zeigt die Datei im Browser an, wenn der Content-Type unterstützt wird. In OpenAPI wird dieser Header im headers-Objekt der Response beschrieben. Das Schema des Headers ist ein String mit einem regulären Ausdruck als pattern, der die erlaubte Syntax des Headerwertes beschreibt. Da der Dateiname variabel ist, reicht ein einfaches description-Feld mit einem Beispielwert oft aus.
Ein wichtiges Detail für internationale Dateinamen: Der einfache filename-Parameter unterstützt keine Nicht-ASCII-Zeichen. Für Dateinamen mit Umlauten oder anderen Sonderzeichen muss filename* nach RFC 5987 verwendet werden: Content-Disposition: attachment; filename*=UTF-8''Bericht%202026-05.pdf. APIs, die Dateien mit benutzerseitigen Originalnamen zurückgeben müssen, sollten beide Parameter setzen: filename als ASCII-Fallback und filename* für die korrekte Darstellung in modernen Clients. In der OpenAPI-Dokumentation sollte dieses Verhalten explizit in der Header-Beschreibung erklärt werden.
# openapi/paths/download.yaml
/api/v1/documents/{id}/download:
get:
operationId: downloadDocument
summary: Download a document by ID
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Document content
headers:
Content-Disposition:
description: >
Attachment with original filename.
Uses filename* (RFC 5987) for non-ASCII names.
Example: attachment; filename*=UTF-8''Bericht%202026.pdf
schema:
type: string
Content-Length:
schema:
type: integer
description: File size in bytes
ETag:
schema:
type: string
description: Unique file version identifier for caching
content:
application/pdf:
schema:
type: string
format: binary
application/octet-stream:
schema:
type: string
format: binary
'404':
$ref: '#/components/responses/NotFound'
7. Mehrere Dateien in einem Request beschreiben
Wenn eine API mehrere Dateien in einem einzigen Request akzeptiert, gibt es in OpenAPI zwei Modellierungsansätze. Der erste: Ein Formularfeld als Array definieren — type: array, items: {type: string, format: binary}. Das entspricht dem HTML-Pattern <input type="file" multiple> und sendet mehrere Parts mit demselben Feldnamen. Der zweite Ansatz: Separate Feldnamen für jede Datei — primaryDocument: {type: string, format: binary} und attachments: {type: string, format: binary}. Dieser Ansatz ermöglicht unterschiedliche Validierungsregeln pro Datei-Slot.
Das maxItems-Constraint auf dem Array-Feld beschränkt die Anzahl der hochladbaren Dateien in der Dokumentation. Auf der Serverseite muss diese Beschränkung separat implementiert werden — OpenAPI-Constraints sind rein dokumentarisch und werden nicht automatisch durchgesetzt. Ein häufiger Fehler: Wenn der Server nur eine Datei pro Request akzeptiert, wird das Array-Muster trotzdem verwendet, weil items: {type: string, format: binary} «offensichtlich richtig» wirkt. Das sollte ein reguläres Einzel-Feld sein, kein Array — Integratoren wählen sonst unnötig komplexe Client-Implementierungen.
8. Client-Generierung mit format: binary
OpenAPI-Client-Generatoren wie openapi-generator und swagger-codegen verarbeiten format: binary unterschiedlich je nach Zielsprache. In TypeScript wird ein Feld mit format: binary als Blob | File generiert. In Python als IO[bytes]. In Java als File-Objekt. Diese Typen erlauben es dem Client, Dateisystem-Objekte direkt zu übergeben, ohne manuelle Kodierung. Wenn das Schema fehlerhaft ist — z.B. format: binary fehlt oder durch format: byte ersetzt wurde — generiert der Client einen string-Parameter, und der Integrator muss die Datei manuell base64-kodieren.
Für Symfony-Backends empfiehlt sich, die generierte Client-Bibliothek regelmäßig zu testen: die generierte TypeScript-Client-Methode sollte direkt ein File-Objekt aus dem Browser-FileReader akzeptieren. Wenn das nicht der Fall ist, liegt ein Fehler in der OpenAPI-Spezifikation vor. Ein praktischer Test: Den Swagger UI für den eigenen Endpoint öffnen und das Datei-Upload-Feld testen. Swagger UI generiert aus einer korrekten Spezifikation ein natürliches Datei-Auswahlfeld. Erscheint stattdessen ein Texteingabefeld, fehlt format: binary oder der Context ist falsch.
| Szenario | Content-Type | Schema | Hinweis |
|---|---|---|---|
| Datei-Upload | multipart/form-data | format: binary | Datei als Part, nicht base64 |
| Kleines Bild in JSON | application/json | format: byte | base64, +33% Größe |
| Generischer Download | application/octet-stream | format: binary | Rohe Binärdaten im Body |
| PDF-Download | application/pdf | format: binary | Spezifischer MIME bevorzugt |
| Mehrere Dateien | multipart/form-data | array, items: binary | Gleicher Feldname, mehrere Parts |
Mironsoft
OpenAPI-Dokumentation, REST API Design und Client-Generierung
OpenAPI-Spezifikationen, die Client-Generatoren wirklich nutzen können?
Wir prüfen und korrigieren OpenAPI-Spezifikationen für Datei-Endpoints, dokumentieren Upload- und Download-Varianten vollständig und validieren die generierten Clients gegen die Implementierung.
Spezifikations-Review
OpenAPI-Dokument auf Fehler bei format, encoding und Response-Headern prüfen
Client-Generierung
TypeScript- und PHP-Clients aus OpenAPI generieren und gegen die API validieren
Dokumentation
Vollständige Upload- und Download-Endpoints mit allen Fehlerfällen dokumentieren
10. Zusammenfassung
Datei-Uploads und Downloads in OpenAPI 3.1 korrekt zu beschreiben erfordert das Verständnis weniger, aber kritischer Unterschiede: format: binary für rohe Binärdaten im Multipart-Kontext, format: byte für base64-kodierte Daten in JSON. Das encoding-Objekt präzisiert, welche MIME-Types für einzelne Formular-Parts akzeptiert werden. Download-Endpoints beschreiben die Response mit dem spezifischsten verfügbaren MIME-Type und dem Content-Disposition-Header in der headers-Sektion. Für internationale Dateinamen müssen sowohl filename als auch filename* (RFC 5987) gesetzt werden.
Der praktische Test für eine korrekte Spezifikation: Swagger UI zeigt für Upload-Felder ein natürliches Datei-Auswahlfeld, und ein generierter TypeScript-Client akzeptiert ein File-Objekt direkt als Parameter. Wenn ein Texteingabefeld erscheint oder der Client einen string-Parameter erwartet, liegt ein Fehler in der Spezifikation vor. Die hier gezeigten YAML-Strukturen sind der direkte Weg zu einer OpenAPI-Dokumentation, die sowohl für Integratoren als auch für Code-Generatoren produktiv nutzbar ist.
Datei-Uploads und Downloads in OpenAPI — Das Wichtigste auf einen Blick
format: binary vs. byte
binary = rohe Binärdaten in multipart/form-data. byte = base64-kodiert in JSON. Verwechslung erzeugt falsche Client-Code-Generierung.
encoding-Objekt
Definiert Content-Type pro Multipart-Part. Ermöglicht JSON-Metadaten neben Binärdaten im selben Request.
Download-Response
application/pdf (oder octet-stream) + schema: {type: string, format: binary}. Content-Disposition im headers-Objekt dokumentieren.
Validierungstest
Swagger UI zeigt Datei-Auswahlfeld. Generierter Client akzeptiert File/Blob-Objekt. Textfeld = Fehler in der Spezifikation.