20.2. Nützliche Funktionen der REST-API

Die REST-API hat ein paar nützliche Funktionen integriert, die für verschiedene Anwendungsgebiete genutzt werden können.

JSONP zurückliefern

JSONP steht für „JSON mit Padding“. Es erlaubt das Nachladen von Inhalten externer Domains. Normalerweise würde dies jeder Browser (Aufgrund der Same-Origin-Policy) blockieren. Mit dem _jsonp-Parameter lässt sich das umgehen. Man muss dazu eine JavaScript-Funktion übergeben, wie folgendes Beispiel zeigt:

<script>
function receive_data( data ) {
  console.log( data );
}
</script>
<script src="https://test.local/wp-json/?_jsonp=receive_data"></script>

Anfrage-Methode überschreiben

Einige Server und auch Clients können bestimmte HTTP-Methoden nicht richtig verarbeiten. Als Beispiel wäre hier die DELETE-Anweisung genannt, die manche Clients gar nicht zur Verfügung stellen.

Um trotzdem eine DELETE-Anweisung senden zu können, gibt es zwei verschiedene Möglichkeiten:

1) _method-Parameter

Man hängt den Parameter _method an die URL an oder übergibt sie mit weiteren POST-Parametern. Beispiel:

POST http://test.local/wp-json/wp/v2/posts/1?_method=delete

2) X-HTTP-Method-Override-Header

Alternativ kann man einen zusätzlichen Header – wie oben genannt – senden:

POST /wp-json/wp/v2/posts/1 HTTP/1.1
Host: test.local
X-HTTP-Method-Override: DELETE

Alle Rücksense-Daten in die Antwort verpacken

Manche Clients können bestimmte Antworten eines Servers nicht richtig verarbeiten. Wenn beispielsweise der Statuscode oder bestimmte Header nicht zurückgegeben werden können, kann man den _envelope-Parameter anhängen:

GET http://test.local/wp-json/wp/v2/posts/1

würde folgendes zurückgeben:

{
    "id": 1,
    "slug": "hallo-welt",
    "status": "publish",
    "type": "post",
    "title": {
        "rendered": "Hallo Welt!"
    },
    ...
}

Mit dem _envelope-Parameter sieht es wie folgt aus. Eine Anfrage an:

GET http://test.local/wp-json/wp/v2/posts/1?_envelope

ergibt:

{
    "body": {
        "id": 1,
        "slug": "hallo-welt",
        "status": "publish",
        "type": "post",
        "link": "http://test.local/2018/01/22/hallo-welt/",
        "title": {
            "rendered": "Hallo Welt!"
        },
        ...
    },
    "status": 200,
    "headers": {
        "Link": "<http://test.local/2018/01/22/hallo-welt/>; rel=\"alternate\"; type=text/html",
        "Allow": "GET"
    }
}

Links

Die meisten Antworten einer Abfrage enthalten weitere Links zu anderen Ressourcen die mit dem Treffer in Relation stehen. Zum Beispiel enthält ein Abruf eines Beitrags

GET http://test.local/wp-json/wp/v2/post/1

auch die Autor-ID, die ID zu einem Beitragsbild und mehr. In der Regel wird jedoch nur die ID des Objekts, welches in der Relation steht, zurückgegeben. Über den _links-Parameter der Antwort erhält man sofort die URL, die man nutzen kann um weitere Abfragen zu starten. Das Beispiel von oben ergibt die Ausgabe (verkürzt):

{
    "id": 1,
    "title": {
        "rendered": "Hallo Welt!"
    },
     ...
    "_links": {
        "collection": [
            {
                "href": "http://test.local/wp-json/wp/v2/posts"
            }
        ],
        "author": [
            {
                "embeddable": true,
                "href": "http://test.local/wp-json/wp/v2/users/1"
            }
        ],
        "replies": [
            {
                "embeddable": true,
                "href": "http://test.local/wp-json/wp/v2/comments?post=1"
            }
        ],
        "version-history": [
            {
                "href": "http://test.local/wp-json/wp/v2/posts/1/revisions"
            }
        ],
        "wp:attachment": [
            {
                "href": "http://test.local/wp-json/wp/v2/media?parent=1"
            }
        ],
        "wp:term": [
            {
                "taxonomy": "category",
                "embeddable": true,
                "href": "http://test.local/wp-json/wp/v2/categories?post=1"
            },
            {
                "taxonomy": "post_tag",
                "embeddable": true,
                "href": "http://test.local/wp-json/wp/v2/tags?post=1"
            }
        ],
		"curies": [
            {
                "name": "wp",
                "href": "https://api.w.org/{rel}",
                "templated": true
            }
        ]
    }
}

Man unterscheidet dabei zwischen drei verschiedenen Arten von Relationen:

  1. Eine standardisierte Relation. Dazu gehört zum Beispiel der Autor. Eine vollständige Liste findet man hier: http://www.iana.org/assignments/link-relations/link-relations.xhtml#link-relations-1
  2. Eine URI-Relation. Hier würde man die gesamte URL als Schlüssel verwenden (siehe Beispiel unten). Allerdings wäre das im Code dann sehr schlecht handhabbar und unübersichtlich. Deswegen nutzt man die CURIE-Relation.
  3. Eine CURIE-(Compact-URI) Relation. Hier werden die Kürzel im Parameter curies übergeben. Damit könnte man sich die URL dann wieder selbst zusammen bauen, falls nötig. Siehe Beispiel unten.

Exkurs URI-Relationen

Beispiel URI-Relation (kommt so in WordPress nicht vor)

{
     ...
    "_links": {
        "https://api.w.org/attachment": [
            {
                "href": "http://test.local/wp-json/wp/v2/media?parent=1"
            }
        ],
       ...
    }
}

In JavaScripte müsste man das attachment-Property in etwa so ansprechen:

data._links["https://api.w.org/attachment"]

Beispiel CURIE-Relation

Aus dem URI-Relation-Beispiel würde folgendes werden:

{
     ...
    "_links": {
        "wp:attachment": [
            {
                "href": "http://test.local/wp-json/wp/v2/media?parent=1"
            }
        ],
		"curies": [
            {
                "name": "wp",
                "href": "https://api.w.org/{rel}",
                "templated": true
            }
        ]
    }
}

Man ergänzt quasi den Parameter curies und stellt so klar, welche Relation gemeint ist. Das wp:attachment würde dann zu https://api.w.org/attachment werden. Der Aufruf ist allerdings deutlich kürzer. In einem JavaScript-Code könnte man den Wert wie folgt ansprechen:

data._links["wp:attachment"]

Die Link-Objekte enthalten alle einen Wert der mit href gekennzeichnet ist. Er enthält die volle URL zum verwandten Objekt. Möchte man also den Autor erhalten, kann man einen Abruf an:

GET http://test.local/wp-json/wp/v2/users/1

senden und erhält damit alle Informationen zum Autor.

Wie man sieht, enthält der Autor noch einen weiteren Wert. Nämlich embeddable: true. Das bedeutet, dass man sich eine Abfrage sparen könnte, wenn man den Parameter _embed an die Ursprungs-URL anhängt. Das funktioniert wie folgt:

Anfragen einsparen

Wie gerade erwähnt, enthalten manche Antworten des Servers Links zu weiteren Inhalten. Hier beispielhaft die Antwort auf die Anfrage einer Seite:

GET http://test.local/wp-json/wp/v2/pages/2

Ergibt:

{
    "id": 2,
    "title": {
        "rendered": "Beispiel-Seite"
    },
    "author": 1,
    "featured_media": 0,
    "parent": 0,
    ...
    "_links": {
        "self": [
            {
                "href": "http://test.local/wp-json/wp/v2/pages/2"
            }
        ],
        "collection": [
            {
                "href": "http://test.local/wp-json/wp/v2/pages"
            }
        ],
        "about": [
            {
                "href": "http://test.local/wp-json/wp/v2/types/page"
            }
        ],
        "author": [
            {
                "embeddable": true,
                "href": "http://test.local/wp-json/wp/v2/users/1"
            }
        ],
        "replies": [
            {
                "embeddable": true,
                "href": "http://test.local/wp-json/wp/v2/comments?post=2"
            }
        ],
        "version-history": [
            {
                "href": "http://test.local/wp-json/wp/v2/pages/2/revisions"
            }
        ],
        "wp:attachment": [
            {
                "href": "http://test.local/wp-json/wp/v2/media?parent=2"
            }
        ],
        "curies": [
            {
                "name": "wp",
                "href": "https://api.w.org/{rel}",
                "templated": true
            }
        ]
    }
}

Möchte man nun an die Daten des Autors kommen, müsste man eine weitere Anfrage an den Webserver senden. Diese weitere Anfrage kann man sich einsparen, wenn man den Parameter _embed anhängt. Dann nämlich wird die REST-API weiteren Daten gleich mit zurückliefern. Eine Anfrage wie folgt:

GET http://test.local/wp-json/wp/v2/pages/2?_embed

liefert dann folgendes:

{
    "id": 2,
    "title": {
        "rendered": "Beispiel-Seite"
    },
    "author": 1,
    "featured_media": 0,
    "parent": 0,
    ...
    "_embedded": {
        "author": [
            {
                "id": 1,
                "name": "florian",
                "url": "",
                "description": "",
                "link": "http://test.local/author/florian/",
                "slug": "florian",
                "avatar_urls": {
                    "24": "http://0.gravatar.com/avatar/c2b06ae950033b392998ada50767b50e?s=24&d=mm&r=g",
                    "48": "http://0.gravatar.com/avatar/c2b06ae950033b392998ada50767b50e?s=48&d=mm&r=g",
                    "96": "http://0.gravatar.com/avatar/c2b06ae950033b392998ada50767b50e?s=96&d=mm&r=g"
                },
                "_links": {
                    "self": [
                        {
                            "href": "http://test.local/wp-json/wp/v2/users/1"
                        }
                    ],
                    "collection": [
                        {
                            "href": "http://test.local/wp-json/wp/v2/users"
                        }
                    ]
                }
            }
        ]
    }
}

Paginierung

In einer WordPress-Datenbank können Unmengen von Daten gespeichert sein. REST-Abfragen können daher auch richtig große Datenmengen zurückgeben. Viel mehr als man verarbeiten möchte. Aus diesem Grund erlauben die meisten API-Abfragen, die eine Liste von Daten zurückgeben, die Angabe der Parameter page, per_page und offset in der URL.

Es gilt:

  • per_page (int)
    Anzahl der Treffer pro Seite.
  • offset (int)
    Der Startpunkt bzw. der Versatz.
  • page (int)
    Die Seite.

Wenn wir pro Seite nur einen Treffer zurückgeben wollen und uns auf der zweite Seiten befinden, könnte man folgendes schreiben um den zweiten Post zu erhalten:

GET http://test.local/wp-json/wp/v2/posts?per_page=1&page=2

Das gleiche Ergebnis würde man mit folgendem Aufruf erhalten:

GET http://test.local/wp-json/wp/v2/posts?per_page=1&offset=1&page=1

Durch offset=1 wurde die Ergebnisliste um 1 verschoben. Wir erhalten auf Seite 1 also den zweiten Beitrag.

Hinweis
Der Parameter per_page wurde intern aus Performancegründen auf das Limit von 100 begrenzt.

Für die Gesamtzahl der vorhandenen Einträge muss man nicht zwingend eine neue Abfrage starten. Diese liefert die REST-API nämlich bereits mit, wenn der page-Parameter angehängt wurde. Und zwar im Antwort-Header:

X-WP-Total →2
X-WP-TotalPages →2

Hinweis
Wenn Sie die Antwort-Header nicht auslesen können, nutzen Sie den _envelope-Parameter wie oben beschrieben.

Sortierung

Ähnlich der Paginierung gibt es zwei Parameter, die Ergebnisse sortieren lassen:

  • order (string)
    asc (aufsteigend) oder desc (absteigend). Standard ist desc.
  • orderby (string)
    Der Parameter nach dem geordnet werden soll. Diese können, je nach Abfrage, unterschiedlich sein. Sehen Sie sich dazu bitte die Schemata der einzelnen Routen genauer an.

Beispiel:

GET http://test.local/wp-json/wp/v2/posts?order=desc&orderby=id

Ordnet alle Beiträge absteigend nach deren ID.

API-Discovery

Man kann testen, ob die REST-API einer Website aktiviert ist oder nicht. Dazu sendet man einen HEAD-Request an die jeweilige Seite:

HEAD http://test.local

Man erhält unter anderem folgende Information im Antwort-Header zurück:

Link →<http://test.local/wp-json/>; rel="https://api.w.org/"

Wenn man keinen HEAD-Request senden kann, geht es auch mit GET. Allerdings muss man dann den zurückkommenden HTML-Code analysieren:

GET http://test.local

Im <head>-Bereich der Website sollte man dann folgendes finden:

<link rel='https://api.w.org/' href='http://test.local/wp-json/' />

Auch über die XMLRPC-Schnittstelle (falls aktiv) lässt sich die URL zur REST-API herausfinden:

GET http://test.local/xmlrpc.php?rsd

gibt folgendes zurück:

<?xml version="1.0" encoding="UTF-8"?>
<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">
    <service>
        <engineName>WordPress</engineName>
        <engineLink>https://wordpress.org/</engineLink>
        <homePageLink>http://test.local</homePageLink>
        <apis>
            ...
            <api name="WP-API" blogID="1" preferred="false" apiLink="http://test.local/wp-json/" />
        </apis>
    </service>
</rsd>

Extension-Discovery

Wenn man die URL zur REST-API herausgefunden hat (siehe oben), kann man automatisiert herausfinden, was denn alles möglich ist (welche Funktionen die Schnittstelle bietet).

Dazu sendet man initial eine Abfrage an die API:

GET http://test.local

und liest den namespace-Parameter aus:

{
    "url": "http://test.local",
    ...
    "namespaces": [
        "oembed/1.0",
        "wp/v2"
    ],
   ...
}

Durch weitere Abfragen erhält man schließlich alle Routen zu den Namespaces:

GET http://test.local/wp/v2

ergibt:

{
    "namespace": "wp/v2",
    "routes": {
        "/wp/v2": {
            "namespace": "wp/v2",
            "methods": [
                "GET"
            ],
            "endpoints": [
                {
                    "methods": [
                        "GET"
                    ],
                    "args": {
                        "namespace": {
                            "required": false,
                            "default": "wp/v2"
                        },
                        "context": {
                            "required": false,
                            "default": "view"
                        }
                    }
                }
            ],
           ...