RFC 9457 problem+json standardisiert Fehler-Responses als JSON-Objekt mit type, title, status, detail und instance, ausgeliefert als application/problem+json — sodass Clients Fehler einheitlich parsen. Pagination teilt große Collections in Seiten mittels eines cursor (stabil) oder offset (einfach), wobei next/prev-Links im RFC-8288-Link-Header getragen werden.
problem+json: ein Standard-Fehlerobjekt
Maßgeschneiderte Fehlerformen zwingen jeden Client, jede API zu sonderbehandeln. RFC 9457 (das RFC 7807 ablöst) legt ein einziges, erweiterbares JSON-Fehlerformat fest, ausgeliefert mit dem Media-Type application/problem+json, sodass ein Client weiß, es als Problem zu parsen. Das Objekt hat fünf Standard-Member: type, eine URI, die die Problemklasse identifiziert und zugleich als Link zur menschlichen Dokumentation dient; title, eine kurze, stabile, menschenlesbare Zusammenfassung dieser Klasse; status, der in den Body gespiegelte HTTP-Statuscode; detail, eine menschenlesbare Erklärung speziell für dieses Vorkommen; und instance, eine URI, die dieses konkrete Vorkommen identifiziert. Beliebige zusätzliche Member (ein validation-errors-Array, eine Request-ID, ein Retry-Hinweis) erweitern das Objekt, ohne die Standard-Member zu brechen, sodass ein generischer Client die gemeinsamen Felder liest und ein spezifischer Client auch die Erweiterungen.
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
{
"type": "https://example/problems/invalid-destination",
"title": "Invalid destination number",
"status": 422,
"detail": "The field 'to' is not a valid E.164 number.",
"instance": "/v1/messages/req-01HZY9",
"errors": [ { "field": "to", "issue": "not_e164" } ]
}Type, title und instance
Die Disziplin, die problem+json nützlich macht, ist, type und title stabil und maschinenorientiert zu halten, während detail und instance je Vorkommen variieren dürfen. Ein Client sollte auf type verzweigen (eine stabile URI), nie auf die Prosa in detail oder title, denn detail darf sich ändern und kann lokalisiert sein. Die type-URI löst idealerweise zur Dokumentation dieser Fehlerklasse auf, sodass ein Entwickler, der ihr folgt, auf einer Erklärung und einem Lösungsweg landet. instance identifiziert den konkreten fehlgeschlagenen Request — nützlich, um ihn in einem Support-Ticket zu zitieren — und spiegelt oft eine Request-ID, die die API auch in einem Header zurückgibt. Weil type und title die Klasse identifizieren und status/detail/instance das Vorkommen beschreiben, kann ein Problem-Typ über viele Fehlschläge hinweg ohne Mehrdeutigkeit wiederverwendet werden.
Fehler-Taxonomie: retryable vs. terminal
Über die Form hinaus muss der Client wissen, was zu tun ist, und die Statuscode-Klasse beantwortet das. Ein 4xx ist ein Client-Fehler: der Request wird, so wie gesendet, nie erfolgreich sein, daher ist ein unveränderter Retry sinnlos — die Lösung ist, den Request zu korrigieren (400/422 bei fehlerhaften Eingaben, 401/403 bei Auth, 404 bei einer fehlenden Ressource, 409 bei einem Konflikt). Ein 5xx ist ein Server- oder Upstream-Fehler: der Request kann später durchaus erfolgreich sein, daher ist er mit Backoff wiederholbar — idealerweise unter demselben Idempotency-Key, sodass der Retry nicht doppelt senden kann. 429 ist der besondere transiente Fall: wiederholbar, aber erst nach der Retry-After-Verzögerung. Dies im Statuscode zu kodieren (und mit einem Problem-Typ zu verstärken) lässt einen Client automatisch zwischen korrigieren-und-neu-senden, zurückweichen-und-wiederholen und aufgeben entscheiden.
| Klasse | Bedeutung | Wiederholbar? | Client-Aktion |
|---|---|---|---|
| 400 / 422 | Fehlerhafte/ungültige Eingabe | No | Anfrage korrigieren, dann erneut senden |
| 401 / 403 | Nicht authentifiziert / verboten | No | Zugangsdaten oder Scope korrigieren |
| 404 | Ressource nicht gefunden | No | Bezeichner korrigieren |
| 409 | Zustandskonflikt (z. B. Wiederverwendung eines Schlüssels) | No | Den Konflikt lösen |
| 429 | Ratenlimit überschritten | Ja (verzögert) | Retry-After abwarten, dann erneut versuchen |
| 500 / 502 / 503 | Server-/Upstream-Fehler | Ja | Backoff + Wiederholungsversuch unter demselben Idempotenzschlüssel |
Pagination: cursor vs. offset
Ein Collection-Endpoint, der Millionen von Nachrichten zurückgeben könnte, muss paginieren. Die beiden verbreiteten Modelle tauschen Einfachheit gegen Korrektheit. Die Offset-Pagination (?limit=50&offset=100) ist trivial und lässt einen Client zu einer beliebigen Seite springen, ist aber auf einer sich ändernden Collection instabil: werden zwischen den Seiten Zeilen eingefügt oder gelöscht, verschieben sich Einträge, und der Client überspringt oder wiederholt Datensätze — und tiefe Offsets werden langsam, weil der Speicher alles davor scannen und verwerfen muss. Die Cursor-(Keyset-)Pagination reicht dem Client einen opaken cursor, der die Position des zuletzt gesehenen Eintrags kodiert (zum Beispiel eine id und einen Zeitstempel); der nächste Request fragt Zeilen nach diesem cursor an. Sie ist stabil unter nebenläufigen Schreibvorgängen und bleibt in jeder Tiefe schnell, weil die Abfrage direkt zum cursor springt, um den Preis, keine beliebigen Seitensprünge zu unterstützen. Für hochvolumige, anhäng-lastige Ressourcen wie Nachrichten-Logs ist die Cursor-Pagination die richtige Voreinstellung.
| Eigenschaft | Offset | Cursor (Keyset) |
|---|---|---|
| Request | ?limit=50&offset=100 | ?limit=50&after=<cursor> |
| Sprung zu beliebiger Seite | Ja | Nein (nur sequenziell) |
| Stabil bei Einfügungen/Löschungen | Nein – Einträge verschieben sich | Ja |
| Deep-Page-Performance | Verschlechtert sich (Scan + Verwerfen) | Konstant (Seek zum Cursor) |
| Ideal für | Kleine, stabile Sammlungen | Große, anhängelastige Logs |
Der Link-Header (RFC 8288)
Statt next-/previous-URLs im Body zu vergraben, kann eine API sie im Standard-Link-Header aus RFC 8288 (Web Linking) tragen und dabei registrierte Relation-Types verwenden — next, prev, first, last. Der Client folgt dem rel="next"-Link, um voranzuschreiten, ohne selbst URLs zu konstruieren oder das cursor-Schema zu kennen, was den Pagination-Mechanismus vollständig serverkontrolliert hält und ihm erlaubt, sich weiterzuentwickeln (heute offset, morgen cursor), ohne Clients zu brechen. Ein verbreiteter Gürtel-und-Hosenträger-Ansatz spiegelt die Links zusätzlich in den JSON-Body für Clients, die keine Header lesen, aber der Link-Header ist ihr standardisiertes Zuhause auf Transportebene und derselbe Mechanismus, den ein Browser oder ein wohlerzogener API-Client bereits versteht.
HTTP/1.1 200 OK
Content-Type: application/json
Link: </v1/messages?after=01HZY8Q3M6&limit=50>; rel="next",
</v1/messages?limit=50>; rel="first"
{ "data": [ /* up to 50 messages */ ],
"page": { "count": 50, "has_more": true } }Page-Size-Limits und -Voreinstellungen
Die Seitengröße muss serverseitig begrenzt werden, nicht dem Client überlassen, sonst wird ein einzelner Request nach einer riesigen Seite zu einem Denial-of-Service-Vektor und einer Quelle unvorhersehbarer Latenz. Das Muster ist eine sinnvolle Voreinstellung (verwendet, wenn der Client limit weglässt) und ein hartes Maximum, auf das der Server still klemmt — ein Client, der mehr verlangt, erhält schlicht das Maximum. Sowohl die effektive Seitengröße als auch, ob weitere Seiten verbleiben (ein has_more-Flag oder das Vorhandensein eines rel="next"-Links), zu bewerben, lässt einen Client sicher schleifen: einfach next folgen, bis es kein next mehr gibt. Kombiniert mit Cursor-Pagination und problem+json-Fehlern ergibt dies eine Collection-API, die in jeder Größenordnung berechenbar zu konsumieren ist. BroadNet dokumentiert seine konkreten Fehlercodes und Pagination für Entwickler.
Häufig gestellte Fragen
Was ist problem+json und warum es verwenden?
problem+json (RFC 9457, das RFC 7807 ablöst) ist ein Standard-JSON-Fehlerformat, ausgeliefert als application/problem+json, mit den Membern type, title, status, detail und instance. Es lässt jeden Client Fehler auf dieselbe Weise parsen, statt eine maßgeschneiderte Form pro API sonderzubehandeln, und es ist erweiterbar, sodass zusätzliche Member hinzugefügt werden können, ohne die Standard-Member zu brechen.
Auf welches problem+json-Feld sollte ein Client verzweigen?
type — eine stabile URI, die die Fehlerklasse identifiziert. title und detail sind menschenlesbar und können sich ändern oder lokalisiert sein, daher sollte Code nie auf ihre Prosa umschalten. Die type-URI löst idealerweise zur Dokumentation dieses Fehlers auf, während status, detail und instance das konkrete Vorkommen beschreiben.
Woher weiß ein Client, ob er einen Fehler wiederholen soll?
Aus der Statusklasse. Ein 4xx ist ein Client-Fehler, der unverändert nicht erfolgreich sein wird, daher korrigieren Sie den Request, statt zu wiederholen. Ein 5xx ist ein Server-/Upstream-Fehler, der später erfolgreich sein kann, daher wiederholen Sie mit Backoff unter demselben Idempotency-Key. 429 ist wiederholbar, aber erst nach der Retry-After-Verzögerung.
Was ist der Unterschied zwischen cursor- und offset-Pagination?
Offset (?limit&offset) ist einfach und unterstützt Sprünge zu jeder Seite, ist aber instabil, wenn zwischen Requests Zeilen eingefügt oder gelöscht werden, und wird bei tiefen Offsets langsam. Cursor-(Keyset-)Pagination reicht einen opaken Marker des zuletzt gesehenen Eintrags weiter und fragt Zeilen danach an — stabil unter nebenläufigen Schreibvorgängen und in jeder Tiefe schnell, aber nur sequenziell. Cursor ist die richtige Voreinstellung für große Nachrichten-Logs.
Wie hilft der RFC-8288-Link-Header bei der Pagination?
Er trägt die URLs für next, prev, first und last in einem Standard-Header mittels registrierter Relation-Types, sodass der Client rel="next" folgt, um voranzuschreiten, ohne URLs zu bauen oder das cursor-Schema zu kennen. Das hält den Pagination-Mechanismus serverkontrolliert und frei zur Weiterentwicklung, und es ist ein Mechanismus, den generische HTTP-Clients bereits verstehen.
Warum die Seitengröße serverseitig deckeln?
Weil eine unbegrenzte Seitengröße einem Request erlaubt, eine enorme Ergebnismenge zu ziehen, was ein Denial-of-Service-Risiko ist und unvorhersehbare Latenz ergibt. Der Server wendet eine Voreinstellung an, wenn limit weggelassen wird, und klemmt still auf ein hartes Maximum, und er bewirbt, ob weitere Seiten verbleiben, sodass Clients schleifen können, indem sie next folgen, bis es fehlt.
Quellenspezifikationen
Diese Seite basiert auf den veröffentlichten Standards. (Wird in einem neuen Tab geöffnet.)