Struktur der Übergabeformate

Codierung der Daten

Alle zu übermittelnden Daten müssen im ASCII-Format und HTTP-konform übertragen werden.
Folgende Daten-Konvertierung ins ASCII-Format wird erwartet:

DatentypFormat
ZeichenkettenErsetzen der reservierten Zeichen gemäss Übergabeformat / HTTP-Standard
Binärbase64-Codierung
DatumswerteISO 8601
BooleanTrue/False
ZahlenDezimaltrennzeichen Punkt, keine weiteren Formatzeichen

URL-Encoded

Content-type:    application/x-www-form-urlencoded
Accept: -


Dieses Format wird zum Senden (zum REST-Server) von Nutzdaten unterstützt. Es gelten die Regeln nach RFC 3986 über die URI-Syntax (http://tools.ietf.org/html/rfc3986

Die Nutzdaten werden in Form von Name-Wert-Paaren übergeben, die jeweils Url-encoded werden müssen. 

Beispiel für die Übergabe der Parameter ABCEinteilung, Name und Geburtsdatum:

Daten:

ABCEinteilung=1&Name=äöüèéàÖÄÜÉÀÈ+@#°§¬|¢´~¦¨][{}-"*ç%&(/=<>,?&Geburtsdatum=2000-10-02T00:00:00.000Z 

Payload:

ABCEinteilung=1&Name=%C3%A4%C3%B6%C3%BC%C3%A8%C3%A9%C3%A0%C3%96%C3%84%C3%9C%C3%89%C3%80%C3%88%2B%40%23%C2%B0%C2%A7%C2%AC%7C%C2%A2%C2%B4%7E%C2%A6%C2%A8%5D%5B%7B%7D%2D%22%2A%C3%A7%25%26%28%2F%3D%3C%3E%2C%3F&Geburtsdatum=2000%2D10%2D02T00%3A00%3A00.000Z

JSON

Content-type:   application/json
Accept:             application/json

Format "compact"

Dieses Format erlaubt bei grosser Anzahl Datensätzen eine reduzierte Nutzdatenmenge und die Übergabe von Zusatzinformationen in den Metadaten.

Accept:             application/json;compact

Senden Client -> REST

Die Nutzdaten werden als JSON-Objekt in Form von Name-Wert-Paaren übergeben. In der einfachen Form sieht das so aus:

Daten:

{
    "ABCEinteilung":"2",
    "Name":"abc"
}

Sollen gleichzeitig Detaildatensätze respektive Substrukturen übergeben werden, so muss das in folgender Form geschehen:

{
    "<Attribut-Name>":"<Attribut-Wert>",
    "<Attribut-Name>":"<Attribut-Wert>",
   .....
   "<Entität-Name>": [

       {
          "<Attribut-Name>":"<Attribut-Wert>",
          "<Attribut-Name>":"<Attribut-Wert>" ,
          ......
       }
    ] 

Dies Sub-Entitäten können beliebig tief verschachtelt werden. Sie müssen aber immer zwingend benannt werden und in Array-Form übergeben werden,

Die Struktur muss zwingend dem Datenmodel entsprechen, d.h die logische Verbindung zwischen den Elementen muss definiert sein, und zwar immer vom übergeordneten Element zum untergeordneten:

Einem Auftrag können Positionen angehängt werden, aber positionen nicht einem Auftrag. Von dieser Regel ausgenommen sind referenzierte Daten, die nur einmal vorkommen. z.B. eine Adresse kann einem Auftrag mitgegeben werden (obwohl der Auftrag Datentechnisch durch die referenzielle Integrität von der Adresse abhängt).

 

Konkret im Beispiel eines Updates auf einen Kurs mit 2 Unterlisten zu Grundausbildungen und Anstellungsarten sieht es so aus:

{
    "BildungsschwerpunktID": 13,
    "GrundausbildungListe": [
        {
           "StichwortID": 7
        },
        {
           "StichwortID": 7
        }
    ],
    "AnstellungsartListe": [
        {
           "StichwortID": 1
        },
        {
           "StichwortID": 2
        },       
        {
           "StichwortID": 3
        }
    ] 
}

Sobald Substrukturen übergeben werden, wird diese als komplett gültige Liste betrachtet, d.h. bei Update-Operationen werden zuerst alle vorhandenen Einträge gelöscht, dann werden alle übergebenen eingefügt.

Als Konsequenz werden mit einer leeren Daten-Liste alle vorhandenen Einträge gelöscht:

    …
    "GrundausbildungListe": [],
    …

Ist dies nicht gewollt und die vorhandene Liste soll nicht verändert werden, darf die Substruktur nicht übergeben werden.

Empfangen REST -> Client

Bei der Ausgabe von Daten wird unterschieden, ob ein einziges Root-Element vorhanden ist, oder ob auf oberster Ebene mehrere Datensätze ausgegeben werden können. Folgende regeln gelten:

AusgabeRequest
Root, einzelnes ObjektEindeutig über die ID identifizierte Entität, z.B. ../rest2/region/12
(GET, PUT, POST und DELETE) 

Methoden mit einem Parameter-Set (keine Mehrfachaufrufe)


Merfache Datensätze, ArrayAlle Views

Alle Abfragen auf Entitäten
(GET mit Filter oder ohne ID im Pfad) 

Methoden mit mehreren Parameter-Sets

Die Datenstrukturen sind dabei identisch zu denjenigen die gesendet werden. 

Beipiele:

GET auf ../rest/mwst:

[
   {
      "ID": 1,
      "Aktiv": true,
      "Bezeichnung": null,
      "ExterneID": null,
   }
]

GET auf ../rest/mwst/1:

{
   "ID": 1,
   "Aktiv": true,
   "Bezeichnung": null,
   "ExterneID": null,
}

Format "fullmeta"

Dieses Format erlaubt bei grosser Anzahl Datensätzen eine reduzierte Nutzdatenmenge und die Übergabe von Zusatzinformationen in den Metadaten.

Accept:             application/json;fullmeta

Senden Client -> REST

Die Nutzdaten werden als JSON-Objekt in Form von Name-Wert-Paaren übergeben. In der einfachen Form sieht das so aus:

Daten:

{
    "ABCEinteilung":"2",
    "Name":"abc"
}

Sollen gleichzeitig Detaildatensätze respektive Substrukturen übergeben werden, so muss das in folgender Form geschehen:

{
    "<Attribut-Name>":"<Attribut-Wert>"
    "childList": [
    {
        "meta": {
            "name": "<Entität-Name>",
            "parameters": [
            {
               "name": "<Parameter-Name>",
            },{
            ...
            ...
            },{
               "childList": [
                {....}]
           }
        }
        "data": [<Array mit den Daten gemäss Parameter>]
    },
    {
        "meta":...
        ... 
    }]
}

Das Element childList entspricht in der Struktur exakt dem resource-Element für Methoden-Aufrufe. Diese Elemente können beliebig tief verschachtelt werden. Definiert werden sie in der Liste der Parameter.

Die Struktur muss zwingend dem Datenmodel entsprechen, d.h die logische Verbindung zwischen den Elementen muss definiert sein, und zwar immer vom übergeordneten Element zum untergeordneten:

 

Konkret im Beispiel eines Updates auf einen Kurs mit 2 Unterlisten zu Grundausbildungen und Anstellungsarten sieht es so aus:

{
    "BildungsschwerpunktID":"13",
    "childlist": [
    {
        "meta": {
            "name": "GrundausbildungListe",
            "parameters": [{
            "name": "StichwortID"
            }]
        },
        "data": [[7], [8]]
    },
    {
        "meta": {
            "name": "AnstellungsartListe",
            "parameters": [{
            "name": "StichwortID"
            }]
        },
        "data": [[1], [2], [3]]
    }
    ]
}

Sobald Substrukturen übergeben werden, wird diese als komplett gültige Liste betrachtet, d.h. bei Update-Operationen werden zuerst alle vorhandenen Einträge gelöscht, dann werden alle übergebenen eingefügt.

Als Konsequenz werden mit einer leeren Daten-Liste alle vorhandenen Einträge gelöscht:

        …
        "name": "AnstellungsartListe",
        "parameters": [{
        "name": "StichwortID"
    }]
    },
    "data": []

Ist dies nicht gewollt und die vorhandene Liste soll nicht verändert werden, darf die Substruktur nicht übergeben werden.

Empfangen REST -> Client

Jede zurückgegeben Datenstruktur besteht aus den Elementen „meta" und „data". Damit wird strikte zwischen den Nutzdaten und den Meta-Informationen unterschieden, die Verarbeitung der Antworten wird vereinheitlicht.

In den Nutzdaten stehen nur die angeforderten Daten. Dieses Element kann auch leer sein, wenn keine Nutzdaten zurückgegeben werden, z.B. bei der Abfrage von Metadaten oder bei Fehlermeldungen.

Der meta-Bereich wird für beschreibende Informationen zu der ausgeführten Operation und/oder den Daten verwendet. Darin stehen z.B. die Feldnamen bei Abfragen, sowie weiterführende Links zu abhängigen Daten.Werden nur die Meta-Daten abgefragt (/model), ist der data-Bereich leer.

Lookups und Referenzen

Es gibt Felder, die nur gewisse Werte als Eingabe erlauben. Das sind typischerweise Status- und Typen-Felder. Im Backend sind diese oft als Aufzählungstypen implementiert, in der Datenbank als Integer, im JSON als allgemeine Zahl.

Bei diesen Feldern wird in den Meta-Daten der Properties und der Parametern ein Lookup-Block ausgegeben, der als Array von Key-Value-Paaren implementiert ist. dieser gibt vor, welche Werte erlaubt sind (= Key), und was sie bedeuten (= Value). Diese Informationen können z.B. zum Abgleich von Stammdaten, zur Benutzerführung und für Eingabevalidierungen eingesetzt werden.

Bei Methodenparametern können Fremdschlüssel vorkommen, die in der Schnittstelle in der Regel als Zahl/Integer definiert sind. Damit der Entwickler weiss, wie er z.B. einen Suchdialog aufbauen muss, wird die Eingenschaft "resource" ausgegeben, analog zu den Links,

Die Rückgabe-Struktur für Entitäten sieht wie folgt aus:

{
    "resource": [{
        "type": "<Resourcentyp "object">",
        "meta": {
            "name": "<Entität-Name im Kontext der Abfrage>",
            "description": "<Beschreibung>",
            "properties": [
            {
                "name": "<Element-Name>",
                "description": "<Beschreibung>",
                "primary": <Ist Primärschlüssel [true|false>,      // optional
                "required": <Ist Pflichtfeld [true|false]>,              // optional
                "type": "<Feldtyp number|string|object|boolean|date-time|base64>",
                "lookup": [                                                          // optional
               {
                    "key": <Wert>,
                    "value": <Benutzertext>
                },{
               .......
                }],
               "links": [
               {
                  "resource": "<Entität-Name ohne Kontext>",
                  "cardinality": <Kardinalität 1|2147483647>,
                  <Wiederholung des Meta-Elements>                // nur nächste ebene, keine Rekursion.
               }]
          },{
              .......
          }]
      }
      "data": [<Array mit den Daten gemäss Properties>]      
    }]
}

Die Rückgabe-Struktur für Views sieht wie folgt aus:

{
    "resource": [{
        "type": "<Resourcentyp "object">",
        "meta": {
            "name": "<Entität-Name im Kontext der Abfrage>",
            "description": "<Beschreibung>",
            "properties": [                                                              // analog zu den Entitäten
            {
                ......
            }],
            "parameters": [
            {
                "name": "<Parameter-Name>",
                "description": "<Beschreibung>",
                "required": <Ist Pflichtfeld [true|false>,                    // optional
                "type": "<Feldtyp number|string|object|boolean|datetime|base64>",
                "cardinality": <Kardinalität 1|2147483647>,
                "lookup": [                                                               // optional
                {
                    "key": <Wert>,
                    "value": <Benutzertext>
                },{
                .......
                }]
            },{
                .......
            }]
        }
        "data": [<Array mit den Daten gemäss den Propterties>].    // Die data-Struktur entspricht exakt derjenigen der properties-Elementen, inkl. Verschachtelung.
    }]
}

Die Rückgabe-Struktur für Methoden sieht wie folgt aus:

{
    "resource": [{
        "type": "<Resourcentyp "object">",
        "meta": {
            "name": "<Entität-Name im Kontext der Abfrage>",
            "description": "<Beschreibung>",
            "parameters": [
            {
                "name": "<Parameter-Name>",
                "description": "<Beschreibung>",
                "required": <Ist Pflichtfeld [true|false>,                              // optional
                "type": "<Feldtyp number|string|object|boolean|datetime|base64>",
                "cardinality": <Kardinalität 1|2147483647>,
                "lookup": [                                                                           // optional
                {
                    "key": <Wert>,
                    "value": <Benutzertext>
                },{
                .......
                }],
                "resource": <Name der Resource, um bei Referenzwerten Eingabehilfen aufbauen zu können>    // optional
            },{
                .......
            }]
        }
        "data": [<Array mit den Daten gemäss den Parametern>].  
    }]
}

Fehlermeldungen werden in einem analogen Format zurückgegeben: 

{
    "resource": [{
        "type": "<Resourcentyp „message">",
        "code": "<ErrorCode mit Errortext>",
        "message": "<Detailtext>"
    }]
}

Die resource-Elemente können sich wiederholen, wenn unterschiedliche Daten-Strukturen in einem Aufruf übergeben werden.


Beispiel einer komplexen Abfrage einiger Adress-Daten, Attributen des Aufwandkontos und der hinterlegten MWST-Details, sowie der vorhandenen Belege der Adresse 1181:

Abfrageresultat
{
	"resource": [{
		"type": "object",
		"meta": {
			"name": "Adresse",
			"description": "",
			"properties": [{
				"name": "AnredeID",
				"description": "Anrede",
				"type": "number"
			},
			{
				"name": "Name",
				"description": "Name",
				"type": "string"
			},
			{
				"name": "Name2",
				"description": "Zweitname",
				"type": "string"
			},
			{
				"name": "ID",
				"description": "",
				"primary": true,
				"required": true,
				"type": "number",
				"links": [{
					"name": "Beleg",
					"resource": "Beleg",
					"cardinality": 2147483647,
					"description": "",
					"type": "object",
					"properties": [{
						"name": "ArtikelTotal",
						"description": "Artikeltotal",
						"type": "number"
					},
					{
						"name": "AuftragsNr",
						"description": "Auftrags-Nr.",
						"type": "number"
					},
					{
						"name": "BelegNr",
						"description": "Nummer",
						"type": "number"
					},
					{
						"name": "ID",
						"description": "",
						"primary": true,
						"required": true,
						"type": "number"
					},
					{
						"name": "AdresseID",
						"description": "Adresse",
						"type": "number"
					}]
				}]
			},
			{
				"name": "KontoIDAufwand",
				"description": "Vorschlag Aufwandskonto",
				"type": "number",
				"links": [{
					"name": "KontoAufwand",
					"resource": "Konto",
					"cardinality": 1,
					"description": "Vorschlag Aufwandskonto",
					"type": "object",
					"properties": [{
						"name": "Bezeichnung",
						"description": "Bezeichnung",
						"type": "string"
					},
					{
						"name": "Kontoart",
						"description": "Kontoart",
						"type": "number"
					},
					{
						"name": "ID",
						"description": "",
						"primary": true,
						"required": true,
						"type": "number"
					},
					{
						"name": "MWSTID",
						"description": "Vorschlag MWST",
						"type": "number",
						"links": [{
							"name": "MWSTToOne",
							"resource": "MWST",
							"cardinality": 1,
							"description": "Vorschlag MWST",
							"type": "object",
							"properties": [{
								"name": "Aktiv",
								"description": "MWST aktiv",
								"type": "boolean"
							},
							{
								"name": "Bezeichnung",
								"description": "Bezeichnung",
								"type": "string"
							},
							{
								"name": "Kuerzel",
								"description": "Kürzel",
								"type": "string"
							},
							{
								"name": "ID",
								"description": "",
								"primary": true,
								"required": true,
								"type": "number"
							}]
						}]
					}]
				}]
			}]
		},
		"data": [[null,
		"Meine AG",
		null,
		[1181,
		[14835.15,
		1024,
		11476,
		4904,
		1181],
		[14835.15,
		1024,
		1024,
		4907,
		1181],
		[42.25,
		null,
		2643,
		4914,
		1181],
		[311.1,
		null,
		2644,
		4915,
		1181],
		[110.3,
		null,
		2645,
		4916,
		1181]],
		[1000008,
		["Einkauf Bier",
		3,
		1000008,
		[2,
		[true,
		"8% MWST",
		"8%",
		2]]]]]]
	}]
}


Customizing, Modellerweiterungen

Die Anwendungen der I-AG sind stark modellbasiert und können somit auch in der REST-Schnittelle individuell angepasst werden.

Typischer Einsatz ist das Abschirmen der internen Struktur um eine vereinfachte Sicht nach aussen zu geben, die für den jeweiligen Fall optimal ist.

Als Beispiel werden das Denormalisieren von Referenzwerten wie Gebindebezeichnug, oder das Berechnen benötigter Daten wie der aktuelle Lagerbestand etc. in einer Bestellposition genannt.

Es ist auch möglich, denormalisierte Daten schreibbar zugänglich zu machen.