logo

Extra Block Types (EBT) - Neue Erfahrung im Layout Builder❗

Extra Block Types (EBT) - gestylte, anpassbare Blocktypen: Diashows, Registerkarten, Karten, Akkordeons und viele andere. Eingebaute Einstellungen für Hintergrund, DOM Box, Javascript Plugins. Erleben Sie die Zukunft der Layouterstellung schon heute.

Demo EBT-Module EBT-Module herunterladen

❗Extra Absatztypen (EPT) - Erfahrung mit neuen Absätzen

Extra Paragraph Types (EPT) - analoger, auf Absätzen basierender Satz von Modulen.

Demo EPT-Module EPT-Module herunterladen

Scroll
22/05/2025, by Ivan

Paginierung kann ein trügerisch komplexes Thema sein. Es ist leicht, in Fallen zu tappen und Best Practices nicht zu befolgen. Diese Seite wird Ihnen helfen, die Paginierung „richtig“ zu machen. Wenn Sie diese Seite lesen und verstehen, wird Ihr Client robuster und zukunftssicherer sein und Ihnen das Leben langfristig erleichtern.

Wenn Sie nur eine Sache aus diesem Leitfaden mitnehmen, dann sollte es sein, dass Sie Ihre eigenen Paginierungs-URLs nicht selbst konstruieren sollten.

Jede paginierte Antwort vom JSON:API-Modul enthält bereits einen Link zur nächsten Seite einer Sammlung, den Sie nutzen können. Sie sollten diesem Link folgen.

Zu Beginn dieses Dokuments betrachten wir einige wichtige Funktionen der API und wie man die Paginierung „richtig“ umsetzt. Am Ende dieses Dokuments finden Sie einige Antworten auf häufige Fragen und Stolpersteine.

Wie?

Jede paginierte Antwort vom JSON:API-Modul enthält direkt Paginierungslinks. Schauen wir uns ein kleines Beispiel an:

{
  "data": [
    {"type": "sample--type", "id": "abcd-uuid-here"},
    {"type": "sample--type", "id": "efgh-uuid-here"}
  ],
  "links": {
    "self": "<collection_url>?page[offset]=3&page[limit]=3",
    "next": "<collection_url>?page[offset]=6&page[limit]=3",
    "prev": "<collection_url>?page[offset]=0&page[limit]=3"
  }
}

Beachten wir ein paar Dinge:

  • Es gibt 3 Paginierungslinks unter dem links-Schlüssel:
    • self: Dies ist die URL für die aktuelle Seite.
    • next: Dies ist die URL für die nächste Seite.
    • prev: Dies ist die URL für die vorherige Seite.
  • Es gibt ein page[limit] von 3, aber es sind nur 2 Ressourcen vorhanden (?!)

Das Vorhandensein oder Fehlen der Paginierungslinks ist bedeutend. Sie sollten wissen:

  1. Wenn der next-Link existiert, gibt es noch weitere Seiten.
  2. Wenn der next-Link nicht existiert, sind Sie auf der letzten Seite.
  3. Wenn der prev-Link existiert, sind Sie nicht auf der ersten Seite.
  4. Wenn weder ein next- noch ein prev-Link existiert, gibt es nur eine Seite.

Auch wenn ein Seitenlimit von 3 existiert, gibt es nur 2 Ressourcen! Das liegt daran, dass eine Entität aus Sicherheitsgründen entfernt wurde. Wir können erkennen, dass es nicht daran liegt, dass nicht genug Ressourcen vorhanden sind, um die Antwort zu füllen, weil wir sehen, dass es einen next-Link gibt. Wenn Sie mehr darüber wissen möchten, wird das unten ausführlicher erklärt.

Okay, nachdem wir einige wichtige Fakten festgestellt haben, überlegen wir, wie wir unseren Client aufbauen sollten. Wir schauen uns dazu etwas Pseudo-JavaScript an. 🧐

Stellen Sie sich vor, Sie möchten eine Auflistung der neuesten Inhalte auf unserer Seite anzeigen und wir haben einige „Premium“-Inhalte. Nur zahlende Abonnenten sollen Premium-Inhalte sehen dürfen. Außerdem haben wir beschlossen, dass wir eine „Top 5“-Komponente wollen. Falls es mehr Inhalte gibt, soll der Nutzer auf einen „Nächste Seite“-Link klicken können, um die nächsten 5 neuesten Inhalte zu sehen.

Eine naive Implementierung könnte folgendermaßen aussehen:

const baseUrl = 'http://example.com';
const path = '/jsonapi/node/content';
const pager = 'page[limit]=5';
const filter = `filter[field_premium][value]=${user.isSubscriber()}`;

fetch(`${baseUrl}${path}?${pager}&${filter}`)
  .then(resp => {
    return resp.ok ? resp.json() : Promise.reject(resp.statusText);
  })
  .then(document => listComponent.setContent(document.data))
  .catch(console.log);

Aber selbst wenn wir das schlechte Error-Handling ignorieren, wissen wir bereits, dass dies keine besonders robuste Implementierung ist.

Wie wir oben gesehen haben, können wir nicht sicher sein, dass eine Antwort 5 Elemente enthält. Wenn 2 dieser Entitäten nicht zugänglich sind (vielleicht sind sie unveröffentlicht), hat unsere „Top 5“-Komponente nur 3 Elemente!

Wir haben auch einen unnötigen Filter. Der Server sollte bereits Inhalte entfernen, die der Benutzer nicht sehen darf. Wenn nicht, hätten wir eine potenzielle Zugriffsumgehung in unserer Anwendung, da ein bösartiger Benutzer die Abfrage leicht ändern könnte, um die „Premium“-Inhalte zu sehen. Stellen Sie immer sicher, dass Sie Zugriffskontrollen auf dem Server durchsetzen; verlassen Sie sich nicht darauf, dass Ihre Abfragen das für Sie übernehmen.

Lassen Sie uns das beheben:

const listQuota = 5;
const content = [];
const baseUrl = 'http://example.com';
const path = '/jsonapi/node/content';
const pager = `page[limit]=${listQuota}`;

const getAndSetContent = (link) => {
  fetch(link)
  .then(resp => {
    return resp.ok ? resp.json() : Promise.reject(resp.statusText);
  })
  .then(document => {
    content.push(...document.data);
    listContent.setContent(content.slice(0, listQuota));

    const hasNextPage = document.links.hasOwnProperty("next");
    if (content.length <= listQuota && hasNextPage) {
      getAndSetContent(document.links.next);
    }

    if (content.length > listQuota || hasNextPage) {
      const nextPageLink = hasNextPage
        ? document.links.next
        : null;
      listComponent.showNextPageLink(nextPageLink);
    }
  })
  .catch(console.log);
}

getAndSetContent(`${baseUrl}${path}?${pager}`)

Zunächst sehen Sie, dass der filter entfernt wurde. Das liegt daran, dass wir davon ausgehen, dass Zugriffskontrollen auf dem Server durchgeführt werden, anstatt uns auf einen Filter zu verlassen. Das ist die einzige sichere Lösung. Wir könnten ihn als Leistungsoptimierung wieder hinzufügen, aber das ist wahrscheinlich nicht notwendig.

Da wir wissen, dass der Server Ressourcen, die für den Benutzer nicht zugänglich sind, einfach entfernt, müssen wir wirklich prüfen, wie viele Ressourcen tatsächlich in der Antwort enthalten sind.

In der „naiven“ Implementierung sind wir davon ausgegangen, dass jede Antwort 5 Elemente enthält. In diesem Beispiel setzen wir nun ein „Kontingent“ von 5 Ressourcen. Nachdem wir unsere Anfragen gemacht haben, prüfen wir, ob wir unser Kontingent erreicht haben oder nicht. Wir prüfen auch, ob der Server noch weitere Seiten hat (das erkennen wir am next-Link, erinnern Sie sich?).

Wenn wir das Kontingent nicht erreicht haben und wir nicht auf der letzten Seite sind, machen wir eine weitere Anfrage, indem wir den next-Link aus dem Dokument extrahieren und verwenden. Es ist wichtig zu beachten, dass wir keine neue URL für die nächste Seite von Hand erstellen. Es ist nicht nötig, das Rad neu zu erfinden, weil der JSON:API-Server das bereits für uns erledigt hat!

Ein weiterer interessanter Punkt ist, dass wir dank fetch (asynchron) den Inhalt der ersten Anfrage schon zu unserer Komponente hinzufügen können, bevor alle Anfragen abgeschlossen sind. Wenn die zweite Anfrage fertig ist, aktualisieren wir die Komponente einfach noch einmal, sodass sie die neu abgerufenen Ergebnisse enthält.

Schließlich stellen wir sicher, dass unsere fiktive listComponent weiß, ob ein „Nächste Seite“-Link angezeigt werden soll. Der Link soll nur angezeigt werden, wenn wir bereits zusätzliche Inhalte haben oder wenn der Server weitere Seiten hat.

Der erste Fall kann auftreten, wenn wir im ersten Request nur 4 Elemente und im zweiten 5 Elemente, aber keinen next-Link erhalten. Dann haben wir insgesamt 9 Elemente, aber unsere listComponent zeigt nur die ersten 5 an. Wir möchten dennoch einen „Nächste Seite“-Link anzeigen, aber keine weiteren Anfragen mehr durchführen. Dazu setzen wir nextPageLink auf null.

Im zweiten Fall – wenn wir doch einen next-Link haben – übergeben wir diesen Link an unsere Komponente, sodass sie ihn für eine nachfolgende Anfrage verwenden kann. Wir möchten diese Anfrage jedoch nur ausführen, wenn der Benutzer tatsächlich auf „Nächste Seite“ klickt, oder?

Die letzten Absätze verdeutlichen ein wirklich wichtiges Konzept... „Nächste Seite“-Links in Ihrem HTML müssen nicht mit API-Seiten korrelieren! Im Gegenteil, es könnte ein Zeichen dafür sein, dass Sie es „falsch“ machen, wenn sie es tun.

Warum ... ?

... kann ich kein Seitenlimit höher als 50 setzen?

Lesen Sie zunächst das oben gezeigte Beispiel. Verstehen Sie, dass JSON:API für jede Entität in einer Antwort individuelle Zugriffskontrollen durchführen muss. Zweitens: Das JSON:API-Modul will „Zero Configuration“ bieten. Sie sollten nichts installieren, ändern oder konfigurieren müssen, um das Modul zu nutzen.

Der Grund hierfür ist der Schutz Ihrer Anwendung vor einem DDoS-Angriff. Wenn ein böswilliger API-Client ein Seitenlimit von 200.000 Ressourcen setzt, müsste das JSON:API-Modul für jede dieser Entitäten Zugriffskontrollen durchführen. Das würde schnell zu Speicherüberläufen und langsamen Antworten führen. Der Server muss daher ein Maximum setzen. Das Limit von 50 wurde relativ willkürlich als runde Zahl gewählt.

Bitte verstehen Sie, dass es viele und lange Diskussionen zu dieser Entscheidung gab und ein Kompromiss zwischen Support-Aufwand, sinnvollen Voreinstellungen und Frontend-Performance gefunden werden musste. Die Maintainer des JSON:API-Moduls sind sich bewusst, dass das nicht für jeden Anwendungsfall ideal ist, sind aber zuversichtlich, dass Sie kaum oder keine Auswirkungen spüren werden, wenn Ihr Client die Empfehlungen in diesen Dokumenten befolgt :)

Wenn Sie dennoch ein höheres Limit wünschen, können Sie das JSON:API Page Limit-Modul verwenden.

... sind nicht X Ressourcen in der Antwort?

Das JSON:API-Modul erlaubt Ihnen, ein Seiten-Limit anzugeben. Das wird oft als Garantie missverstanden, dass eine bestimmte Anzahl Ressourcen in der Antwort enthalten ist. Zum Beispiel wissen Sie vielleicht, dass genügend Ressourcen verfügbar sind, um die Antwort „aufzufüllen“, aber dennoch enthält die Antwort weniger Ressourcen als erwartet.

Aus vielen der oben genannten Gründe führt JSON:API nur eine Datenbankabfrage für die mit dem page[limit]-Parameter angegebene Anzahl durch. Das ist nur ein Maximum. Wenn der Zugriff auf einige Ressourcen im Ergebnis der Abfrage nicht erlaubt ist, werden diese Ressourcen aus der Antwort entfernt. In diesem Fall sehen Sie weniger Ressourcen als erwartet.

Das ist recht häufig, wenn Sie eine Anfrage für Entitäten stellen, die unveröffentlicht sein könnten (wie Nodes), und diese Entitäten nicht bereits mit dem filter-Parameter herausgefiltert wurden.

 

Artikel aus der Drupal Dokumentation.