Payloads und Antwortformate
Kurzantwort: Jede schreibende Anfrage schickt ein WritePayload mit den
Feldern data (das Objekt) und response (welche Felder zurückkommen sollen).
Lesende Anfragen schicken ein ReadPayload (response, exclude), Listen
zusätzlich parameter (Filter, Sortierung, Pagination). Antworten sind
SingleResponse (ein Objekt) oder QueryResponse (Liste plus meta).
Die Payload-Hierarchie
Abschnitt betitelt „Die Payload-Hierarchie“classDiagram
class ReadPayload {
+List response
+List~String~ exclude
}
class RestListPayload {
+ListSearchParameter parameter
}
class ModelResponse {
+String field
}
class WritePayload~T~ {
+T data
+CreateReadMode createReadMode
}
class PatchPayload {
+HashMap data
}
class AbstractPayload {
+UUID id
+UUID _internalId
}
ReadPayload <|-- RestListPayload
RestListPayload <|-- ModelResponse
ReadPayload <|-- WritePayload
ReadPayload <|-- PatchPayload
AbstractPayload <|-- CreatePayload
AbstractPayload <|-- UpdatePayload
| Payload | benutzt für |
|---|---|
ReadPayload | POST /read/{id} |
RestListPayload | POST /query, POST /{id}/history |
WritePayload<CreatePayload> | POST /create |
WritePayload<UpdatePayload> | PUT /update/{id} |
PatchPayload | PATCH /update/{id} |
ModelResponse | verschachtelter Response Request innerhalb von response |
POST /api/rest/crm/customer/create{ "data": { "name": "Muster GmbH", "email": "info@muster.de" }, "response": ["id", "name"]}idgehört nicht in ein Create-Payload; sie wird erzeugt.- Optional steuert
createReadMode(STRICT|LENIENT) das Verhalten, wenn das Zurücklesen nach dem Anlegen fehlschlägt – siehe Transaktionen.
Update (PUT) – ersetzt
Abschnitt betitelt „Update (PUT) – ersetzt“PUT /api/rest/crm/customer/update/{id}{ "data": { "id": "5a2b…", "name": "Muster GmbH", "email": "neu@muster.de" }, "response": ["+"]}id ist Pflicht (@NotNull(groups = OnUpdate.class)). Was im data-Objekt
fehlt, wird geleert – siehe Schreibsemantik.
Patch – ändert nur das Genannte
Abschnitt betitelt „Patch – ändert nur das Genannte“PATCH /api/rest/crm/customer/update/{id}{ "data": { "id": "5a2b…", "email": "neu@muster.de" }, "response": ["+"]}data ist hier eine freie Map, kein typisiertes Objekt. Nur dadurch kann
PATCH zwischen „Feld fehlt” (unverändert) und „Feld ist null” (leeren)
unterscheiden.
Falle: Ein generiertes Payload-Objekt an PATCH zu schicken, serialisiert alle nicht gesetzten Felder als
null– und leert sie damit. Siehe Schreibsemantik.
POST /api/rest/crm/customer/read/{id}{ "response": ["+", {"field": "orders", "response": ["id", "total"]}], "exclude": ["internalNote"]}POST /api/rest/crm/customer/query{ "response": ["id", "name"], "parameter": { "meta": true, "page": 0, "limit": 25, "order": [{"field": "name", "order": "ASC"}], "query": { "type": "AND", "filter": [{"key": "name", "value": "Muster", "param": "LIKE"}], "group": [] } }}Details zu parameter in Suche und Filter.
Dateien im Payload
Abschnitt betitelt „Dateien im Payload“Zwei Wege:
1. Multipart – POST /create/upload mit den Teilen data (JSON) und
files (eine oder mehrere Dateien). Die Zuordnung läuft über den
Dateinamen: das Feld name im Datei-Objekt muss dem Originalnamen der
hochgeladenen Datei entsprechen. Doppelte Dateinamen werden abgelehnt
(duplicate-filenames|…), fehlende Namen ebenso (missing-filename).
2. Base64 im JSON – jedes Objekt im Payload, das einen content-String
trägt, wird als Datei erkannt, dekodiert und in eine Temp-Datei geschrieben. Der
Name kommt aus name, filename oder fileName. Ein data:-Präfix wird
abgeschnitten. Das funktioniert in beliebiger Verschachtelungstiefe.
Antwortformate
Abschnitt betitelt „Antwortformate“SingleResponse
Abschnitt betitelt „SingleResponse“{ "data": { "id": "5a2b…", "name": "Muster GmbH" }, "meta": { "error": false, "errorMessage": null, "notNull": false }}QueryResponse
Abschnitt betitelt „QueryResponse“{ "data": [ { "id": "…" }, { "id": "…" } ], "meta": { "error": false, "errorMessage": null, "totalCount": 42, "currentPage": 0, "currentPageSize": 25, "currentLimit": 25 }}totalCount wird nur berechnet, wenn parameter.meta = true (Standard).
AuditQueryResponse
Abschnitt betitelt „AuditQueryResponse“{ "data": [ { "revision": { "id": "…", "name": "Muster GmbH" }, "revisionMeta": { "ref": 17, "ts": 1785574268973, "ip": "10.0.0.5", "useragent": "Mozilla/5.0…", "username": "d.mertins" }, "revisionType": "MOD" } ], "meta": { "totalCount": 3, "currentPage": 0, "currentPageSize": 0, "currentLimit": 0 }}Siehe Auditing.
{ "error": "ApiValidationException", "message": "validation-failed", "messageKey": "CDMS_VALIDATION_FAILED", "code": "422", "layer": "API", "violations": [ { "field": "name", "rule": "cannot-be-null" }, { "field": "orders.0.total", "rule": "min-number" } ]}Siehe Fehler und Statuscodes.
Diskriminator @type
Abschnitt betitelt „Diskriminator @type“Bei abstrakten Modellen trägt das Payload-Objekt den konkreten Typ:
{ "data": { "@type": "model.datamodel", "name": "Customer" }, "response": ["+"] }- Top-Level-Payloads verwenden im Hub-Frontend den kurzen Modellnamen
(
system,folder), verschachtelte den qualifizierten (enums.enumitem). - Beim Lesen ist
@typenicht nötig – die Auflösung läuft polymorph über die ID. - Beim rekursiven Anlegen einer abstrakten Referenz im PATCH ist
@typePflicht, sonstmissing-type-for-abstract-field|<feld>.