JSON API - Wir arbeiten nach der Spezifikation

    In letzter Zeit wurde die Webentwicklung aufgeteilt. Jetzt sind wir nicht alle Full-Stack-Programmierer - wir sind Front-End- und Back-End-Programmierer. Und das Schwierigste dabei ist, wie auch anderswo, das Problem der Interaktion und Integration.

    Das Frontend mit dem Backend interagieren über die API. Und das gesamte Entwicklungsergebnis hängt davon ab, um welche API es sich handelt, wie gut oder schlecht sich Backend und Frontend geeinigt haben. Wenn wir alle anfangen, gemeinsam zu diskutieren, wie das Upgrade durchgeführt werden soll, und den ganzen Tag damit verbringen, es zu überarbeiten, werden wir möglicherweise nicht mit Geschäftsaufgaben fertig.

    Um Holivare nicht wegen der Variablennamen zum Stillstand zu bringen und zu züchten, benötigen Sie eine gute Spezifikation. Sprechen wir darüber, wie es sein sollte, allen das Leben zu erleichtern. Gleichzeitig werden wir Experten für Fahrradschuppen.



    Fangen wir von weitem an - mit dem Problem, das wir lösen.

    Vor langer Zeit, im Jahr 1959, hat Cyril Parkinson (nicht zu verwechseln mit der Krankheit, er ist Schriftsteller und eine Wirtschaftsfigur) einige interessante Gesetze ausgearbeitet. Zum Beispiel, dass die Ausgaben mit dem Einkommen wachsen, etc. Einer von ihnen heißt das Gesetz der Trivialität:

    Die Zeit, die zur Diskussion des Gegenstands aufgewendet wird, ist umgekehrt proportional zum betrachteten Betrag.

    Parkinson war Wirtschaftswissenschaftler, und so erklärte er seine Gesetze in wirtschaftlicher Hinsicht. Wenn Sie zum Vorstand kommen und sagen, dass Sie 10 Millionen US-Dollar für den Bau eines Kernkraftwerks benötigen, wird dieses Problem höchstwahrscheinlich viel weniger diskutiert als die Zuweisung von 100 Pfund für einen Fahrradschuppen für Mitarbeiter. Weil jeder weiß, wie man einen Fahrradschuppen baut, jeder seine eigene Meinung hat, jeder sich wichtig fühlt und mitmachen will und das Atomkraftwerk etwas Abstraktes und Fernes ist, sind 10 Millionen auch noch nie gesehen worden - es gibt weniger Fragen.

    1999 tauchte das Parkinson-Gesetz der Trivialität in der Programmierung auf, die dann aktiv weiterentwickelt wurde. In der Programmierung wurde dieses Gesetz hauptsächlich in der englischsprachigen Literatur gefunden und klang wie eine Metapher. Er wurde gerufen Der Bikeshed-Effekt (Effekt eines Fahrradschuppens), aber das Wesentliche ist dasselbe - wir sind bereit für einen Fahrradschuppen und wollen viel länger diskutieren als den Bau eines Kraftwerks.

    Dieser Begriff wurde vom dänischen Entwickler Poul-Henning Kamp geprägt, der an der Entwicklung von FreeBSD beteiligt war. Während des Designprozesses diskutierte das Team sehr lange, wie die Schlaffunktion funktionieren sollte. Dies ist ein Zitat aus einem  Brief von Poul-Henning Kamp (Entwicklung erfolgte dann in E-Mail-Korrespondenz):

    Es war ein Vorschlag, Schlaf zu machen. (1) DTRT Wenn ein nicht ganzzahliges Argument angegeben wird, das dieses Grasfeuer auslöst, werde ich nicht mehr darüber sagen, weil es ein viel kleineres Element ist als eines Erwarten Sie von der Länge des Threads, und er hat bereits weit mehr Aufmerksamkeit erhalten als einige der * Probleme *, die wir hier haben.

    In diesem Brief sagt er, dass es viele viel wichtigere ungelöste Probleme gibt: "Lasst uns nicht mit dem Fahrradschuppen fertig werden, wir werden etwas damit machen und

    weitermachen !" das kann umformuliert werden als:

    Das durch die Änderung des Codes verursachte Rauschen ist umgekehrt proportional zur Komplexität der Änderung.

    Je einfacher das Hinzufügen oder Ändern ist, desto mehr Meinungen müssen wir dazu hören. Ich denke, viele haben dies getroffen. Wenn wir eine einfache Frage lösen, zum Beispiel, wie man Variablen benennt, spielt es für eine Maschine keine Rolle - diese Frage wird eine große Anzahl von Holivars verursachen. Aber ernsthafte, für Unternehmen wirklich wichtige Probleme werden nicht besprochen und treten in den Hintergrund.

    Was ist Ihrer Meinung nach wichtiger: die Art und Weise, wie wir zwischen dem Backend und dem Frontend kommunizieren, oder die Geschäftsaufgaben, die wir erledigen? Jeder denkt anders, aber jeder Kunde, der erwartet, dass Sie ihm Geld bringen, sagt: „Tun Sie mir unsere Geschäftsaufgaben schon!“ Es ist ihm egal, wie Sie Daten zwischen dem Backend und dem Frontend übertragen. Vielleicht weiß er nicht einmal, was ein Backend und ein Frontend sind.

    Um die Einleitung zusammenzufassen, möchte ich die Aussage:API ist ein Fahrradschuppen.


    Link zur Präsentation des Berichts

    Über den Sprecher: Alexey Avdeev ( Avdeev ) arbeitet in der Firma Neuron.Digital, die sich mit Neuronen befasst und ihnen ein cooles Frontend bietet. Alex achtet auch auf OpenSource und berät alle. Er hat sich lange Zeit mit der Entwicklung beschäftigt - seit 2002 entdeckte er das alte Internet, als Computer groß waren, das Internet klein und das Fehlen von JS störte niemanden und alle machten Websites auf Tischen.

    Wie gehe ich mit Fahrradschuppen um?


    Nachdem der angesehene Cyril Parkinson das Gesetz der Trivialität abgeleitet hatte, wurde er viel diskutiert. Es stellt sich heraus, dass die Wirkung eines Fahrradschuppens hier leicht vermieden werden kann:

    1. Höre nicht auf Ratschläge. Ich denke, die Idee ist so lala - wenn Sie nicht auf die Ratschläge hören, können Sie viel davon machen, besonders beim Programmieren und besonders, wenn Sie ein Anfänger sind.
    2. Mach was du willst. "Ich bin ein Künstler, ich sehe das!" - kein Bikeshed-Effekt, alles, was benötigt wird, ist erledigt, aber sehr seltsame Dinge erscheinen auf der Ausgabe. Dies ist häufig in freiberuflichen. Sicherlich sind Sie auf Aufgaben gestoßen, die Sie für andere Entwickler erledigen mussten und deren Umsetzung Sie verwirrte.
    3. Ist es wichtig, sich zu fragen? Wenn nicht, können Sie es einfach nicht diskutieren, aber es ist eine Frage des persönlichen Bewusstseins.
    4. Verwenden Sie objektive Kriterien. Ich werde auf diesen Punkt im Bericht eingehen. Um die Wirkung eines Fahrradschuppens zu vermeiden, können Sie Kriterien verwenden, die objektiv aussagen, welche besser sind. Sie existieren.
    5. Sprechen Sie nicht über das, was Sie nicht hören möchten. In unserem Unternehmen sind anfängliche Back-End-Entwickler introvertiert. Es kommt daher vor, dass sie etwas tun, wovon sie anderen nichts erzählen. Infolgedessen stoßen wir auf Überraschungen. Diese Methode funktioniert, aber in der Programmierung ist es nicht die beste Option.
    6. Wenn Sie sich nicht für das Problem interessieren, können Sie es einfach loslassen oder eine der vorgeschlagenen Optionen auswählen , die sich im Verlauf von holivars ergeben haben.

    Anti-Bikeshedding-Tool


    Ich möchte über objektive Werkzeuge zur Lösung des Problems eines Fahrradschuppens sprechen . Um zu demonstrieren, was ein Anti-Bikeshedding-Tool ist, erzähle ich Ihnen eine kleine Geschichte.

    Stellen Sie sich vor, wir haben einen unerfahrenen Backend-Entwickler. Er ist kürzlich in das Unternehmen eingetreten und wurde beauftragt, einen kleinen Dienst zu entwerfen, beispielsweise einen Blog, für den Sie ein REST-Protokoll schreiben müssen.

    Roy Fielding, Autor von REST.

    Auf dem Foto verteidigte Roy Fielding im Jahr 2000 seine Dissertation "Architekturstile und Design von Netzwerksoftwarearchitekturen" und führte damit den Begriff REST ein. Außerdem hat er HTTP erfunden und ist einer der Begründer des Internets.

    REST ist eine Reihe von Architekturprinzipien, die festlegen, wie REST-Protokolle, REST-APIs und RESTful-Services entworfen werden. Dies sind recht abstrakte und komplexe architektonische Prinzipien. Ich bin sicher, dass keiner von Ihnen jemals eine API gesehen hat, die vollständig in Übereinstimmung mit allen RESTful-Prinzipien hergestellt wurde.

    REST-Architekturanforderungen


    Ich werde einige Anforderungen für  REST- Protokolle angeben , auf die ich mich dann beziehen und auf die ich mich stützen werde. Es gibt eine ganze Reihe von ihnen, auf Wikipedia können Sie mehr darüber lesen.

       1. Das Client-Server-Modell.
    Das wichtigste Prinzip von REST ist die Interaktion mit dem Backend. Laut REST ist das Backend ein Server, das Frontend ein Client und wir kommunizieren im Client-Server-Format. Mobile Geräte sind auch ein Client. Entwickler für Uhren, für Kühlschränke, andere Dienstleistungen - entwickeln auch den Kundenteil. Die RESTful-API ist der Server, auf den der Client zugreift.

       2. Mangel an Kondition.
    Auf dem Server darf kein Status vorhanden sein, das heißt, in der Anforderung ist alles enthalten, was für eine Antwort erforderlich ist. Wenn eine Sitzung auf dem Server gespeichert ist und abhängig von dieser Sitzung unterschiedliche Antworten eingehen, verstößt dies gegen das REST-Prinzip.

       3. Einheitlichkeit der Schnittstelle.
    Dies ist eines der wichtigsten Prinzipien, auf denen die REST-API aufbauen sollte. Es umfasst Folgendes:

    • Ressourcenidentifikation ist, wie wir eine URL erstellen sollten. Bei REST wenden wir uns an den Server, um eine Ressource zu erhalten.
    • Manipulation von Ressourcen durch Präsentation. Der Server gibt uns eine Ansicht zurück, die sich von der in der Datenbank unterscheidet. Es ist egal, ob Sie Informationen in MySQL oder PostgreSQL speichern - wir haben eine Ansicht.
    • "Selbstbeschreibende" Nachrichten - das heißt, die Nachricht enthält IDs und Links, über die Sie diese Nachricht erneut abrufen können - alles, was für die erneute Arbeit mit dieser Ressource erforderlich ist.
    • Hypermedia ist ein Link zu den folgenden Aktionen mit einer Ressource. Es scheint mir, dass es keine einzige REST-API gibt, aber sie wird von Roy Fielding beschrieben.

    Es gibt drei weitere Prinzipien, die ich nicht zitiere, weil sie für meine Geschichte nicht wichtig sind.

    RESTful Blog


    Zurück zu den Anfängen des Backend-Entwicklers, der gebeten wurde, einen Service für den Blog auf RESTful zu erstellen. Unten sehen Sie ein Beispiel eines Prototyps.



    Dies ist eine Seite, auf der es Artikel gibt, die Sie kommentieren können. Der Artikel und die Kommentare haben einen Autor - eine Standardgeschichte. Unser neuer Backend-Entwickler wird eine RESTful-API für diesen Blog erstellen.

    Wir arbeiten mit allen Blogdaten auf Basis von CRUD .

    Es sollte möglich sein, Ressourcen zu erstellen, zu lesen, zu aktualisieren und zu löschen. Lassen Sie uns versuchen, unseren Backend-Entwickler aufzufordern, einen RESTful-AP zu erstellen, der auf dem CRUD-Prinzip basiert. Das heißt, schreiben Sie Methoden, um Artikel zu erstellen, eine Liste von Artikeln oder einen einzelnen Artikel abzurufen, zu aktualisieren und zu löschen.

    Mal sehen, wie er es schaffen kann.


    In Bezug auf alle Prinzipien von REST stimmt hier alles nicht.. Das Interessanteste ist, dass es funktioniert. Ich habe tatsächlich APIs bekommen, die ungefähr so ​​aussahen. Für den Kunden ist es ein Fahrradschuppen, für Entwickler ist es eine Gelegenheit, sich zu entspannen und zu streiten, und für einen unerfahrenen Entwickler ist es nur eine riesige, schöne neue Welt, über die er jedes Mal stolpert, fällt und sich den Kopf zertrümmert. Er muss es immer und immer wieder wiederholen.


    Dies ist eine REST-Option. Basierend auf den Prinzipien der Identifizierung von Ressourcen arbeiten wir mit Ressourcen - mit Artikeln und verwenden die von Roy Fielding vorgeschlagenen HTTP-Methoden. Er konnte nicht anders, als seine vorherigen Arbeiten in seinen nächsten Arbeiten zu verwenden.

    Viele verwenden zum Aktualisieren von Artikeln die PUT-Methode, deren Semantik sich geringfügig unterscheidet. Die PATCH-Methode aktualisiert die übergebenen Felder und der PUT ersetzt einfach einen Artikel durch einen anderen. Durch die Semantik wird PATCH zusammengeführt und PUT wird ersetzt.

    Unser unerfahrener Backend-Entwickler ist gestürzt, sie haben es aufgegriffen und gesagt: "Alles ist in Ordnung, mach es so", und er hat es ehrlich überarbeitet. Aber dann wird er einen weiten Weg durch die Dornen finden.

    Warum ist es so richtig?

    • weil Roy Fielding es sagte;
    • weil es REST ist;
    • denn das sind die architektonischen Prinzipien, auf denen unser Beruf jetzt beruht.

    Dies ist jedoch ein "Fahrradschuppen", und die vorherige Methode wird funktionieren. Computer kommunizierten vor REST, und alles funktionierte. Aber jetzt ist ein Standard in der Industrie erschienen.

    Löschen Sie den Artikel


    Betrachten Sie das Beispiel zum Löschen eines Artikels. Angenommen, es gibt eine normale Ressourcenmethode DELETE / articles, mit der der Artikel nach ID entfernt wird. HTTP enthält Header. Der Accept-Header akzeptiert den Datentyp, den der Client als Antwort erhalten möchte. Unser Junior hat einen Server geschrieben, der 200 OK zurückgibt, Content-Type: application / json, und einen leeren Körper übergibt: Hier wurde ein sehr häufiger Fehler gemacht - ein leerer Körper . Alles scheint logisch zu sein - der Artikel wurde gelöscht, 200 OK, der application / json-Header ist vorhanden, aber der Client wird höchstwahrscheinlich fallen. Es wird ein Fehler ausgegeben, da ein leerer Körper nicht gültig ist. Wenn Sie jemals versucht haben, eine leere Zeichenfolge zu analysieren, werden Sie mit der Tatsache konfrontiert, dass ein JSON-Parser darauf stößt und abstürzt.

    01.  DELETE /articles/1 НТТР/1.1
    02.  Accept: application/json


    01.  HTTP/1.1 200 OK
    02.  Content-Type: application/json
    03.  null



    Wie kann ich diese Situation beheben? Wahrscheinlich ist die beste Option, um Json zu übergeben. Wenn wir sagten: "Accept, give us json", sagt der Server: "Content-Type, I give you json", give json. Ein leeres Objekt, ein leeres Array - legen Sie etwas dort ab - dies wird die Lösung sein und es wird funktionieren.

    Es gibt noch eine Lösung. Zusätzlich zu 200 OK gibt es einen Antwortcode 204 - kein Inhalt. Mit ihm können Sie den Körper nicht übertragen. Nicht jeder weiß davon.

    Also habe ich zu den Medientypen geführt.

    MIME-Typen


    Medientypen sind wie eine Dateierweiterung, nur im Web. Wenn wir Daten übermitteln, müssen wir mitteilen oder anfordern, welchen Typ wir als Antwort erhalten möchten.

    • Standardmäßig ist dies Text / Nur-Text.
    • Wenn nichts angegeben ist, bedeutet der Browser höchstwahrscheinlich application / octet-stream - nur ein Bitstream.

    Sie können nur einen bestimmten Typ angeben:

    • application / pdf;
    • image / png;
    • Anwendung / json;
    • application / xml;
    • Anwendung / vnd.ms-Excel.

    Content-Type- und Accept-Header sind und sind wichtig.

    Die API und der Client müssen die Header Content-Type und Accept übergeben.

    Wenn Ihre API auf JSON basiert, übergeben Sie immer Accept: application / json und Content-Type application / json.

    Beispieldateitypen.


    Medientypen ähneln diesen Dateitypen nur im Internet.

    Antwortcodes


    Das nächste Beispiel für die Abenteuer unserer Junior-Entwickler sind Antwortcodes.



    Die lustigste bis t d antworten - 200 OK. Jeder liebt ihn - es bedeutet, dass alles richtig lief. Ich hatte sogar einen Fall - ich habe Fehler 200 OK erhalten . Auf dem Server ist tatsächlich etwas abgestürzt. Als Antwort auf die Antwort kommt eine HTML-Seite, auf der ein HTML-Fehler kompiliert wurde. Ich habe eine Anwendung json mit dem Code 200 OK angefordert und habe darüber nachgedacht, wie ich damit arbeiten soll. Sie gehen durch Antwort, suchen Sie nach dem Wort "Fehler", Sie denken, dass dies ein Fehler ist.

    Dies funktioniert jedoch, es gibt viele andere Codes, die in HTTP verwendet werden können, und Roy Fielding empfiehlt, sie in REST zu verwenden. Zum Beispiel kann die Erstellung einer Entität (eines Artikels) beantwortet werden:

    • 201 Created  ist ein erfolgreicher Code. Der Artikel wird erstellt. Als Antwort müssen Sie den erstellten Artikel zurückgeben.
    • 202 Akzeptiert bedeutet, dass die Anfrage akzeptiert wurde, das Ergebnis jedoch später. Dies sind langfristige Vorgänge. Bei Akzeptiert kann keine Nachricht zurückgegeben werden. Das heißt, wenn Sie den Inhaltstyp in der Antwort nicht angeben, ist dies möglicherweise auch nicht der Körper. Oder Inhaltstyp Text / Ebene - das ist alles, keine Fragen. Eine leere Zeichenfolge ist ein gültiger Text / eine gültige Ebene.
    • 204 No Content  - Der Körper kann völlig fehlen.
    • 403 Verboten  - Sie dürfen diesen Artikel nicht erstellen.
    • 404 Not Found  - Sie sind irgendwo falsch geklettert, so etwas gibt es zum Beispiel nicht.
    • 409 Konflikt  ist ein extremer Fall, den nur wenige Menschen nutzen. Es wird manchmal benötigt, wenn Sie eine ID auf dem Client und nicht auf dem Backend generieren und zu diesem Zeitpunkt bereits jemand diesen Artikel erstellt hat. Konflikt ist in diesem Fall die richtige Antwort.

    Erstellung von Entitäten


    Das folgende Beispiel: Wir erstellen eine Entität, sagen Content-Type: application / json, und übergeben diese application / json. Das macht den Kunden zu unserem Frontend. Angenommen, wir erstellen genau diesen Artikel: Als Antwort könnte der Code kommen:

    01.  POST /articles НТТР/1.1
    02.  Content-Type: application/json
    03.  { "id": 1, "title": "Про JSON API"}




    • 422 Nicht verarbeitbare Entität - Eine nicht verarbeitete Entität. Alles scheint großartig zu sein - Semantik, es gibt Code;
    • 403 Verboten
    • 500 Interner Serverfehler.

    Aber es ist absolut unverständlich, was genau passiert ist: Welche Art von Entität wird nicht verarbeitet, warum sollte ich nicht dorthin gehen und was ist schließlich mit dem Server passiert?

    Rückgabefehler


    Stellen Sie sicher (und die Junioren wissen nichts darüber), dass Sie als Antwort Fehler zurückgeben. Das ist semantisch und richtig. Fielding hat übrigens nicht darüber geschrieben, das heißt, es wurde später erfunden und auf REST aufgebaut.

    Das Backend kann ein Array mit Fehlern als Antwort zurückgeben, es können mehrere vorhanden sein. Jeder Fehler kann einen eigenen Status und Titel haben. Das ist großartig, aber es geht bereits auf der Convention-Ebene über REST. Dies könnte unser Anti-Bikeshedding-Tool sein, das aufhört zu streiten und sofort eine gute, richtige API erstellt.

    01.  HTTP/1.1 422 Unprocessable Entity
    02.  Content-Type: application/json
    03. 
    04.  { "errors": [{
    05.    "status": "422",
    06.    "title": "Title already exist",
    07.  }]}




    Paginierung hinzufügen


    Folgendes Beispiel: Designer kommen zu unserem anfänglichen Backend-Entwickler und sagen: „Wir haben viele Artikel, wir brauchen Paginierung. Wir haben diesen gezeichnet. “


    Lassen Sie es uns genauer betrachten. Zunächst fallen 336 Seiten auf. Als ich das sah, dachte ich darüber nach, wie ich diese Figur bekommen könnte. Wo bekomme ich 336, denn wenn ich eine Artikelliste anfordere, erhalte ich eine Artikelliste. Zum Beispiel gibt es zehntausende von ihnen, das heißt, ich muss alle Artikel herunterladen, durch die Anzahl der Seiten dividieren und diese Anzahl herausfinden. Ich werde diese Artikel für eine sehr lange Zeit laden, ich brauche eine Möglichkeit, um die Anzahl der Einträge schnell zu erhalten. Wenn unsere API jedoch eine Liste zurückgibt, geben Sie an, wo diese Anzahl von Datensätzen überhaupt abgelegt werden soll, da eine Reihe von Artikeln als Antwort eingeht. Es stellt sich heraus, dass, da die Anzahl der Einträge nirgendwo steht, sie zu jedem Artikel hinzugefügt werden muss, damit in jedem Artikel steht: „Und wir alle sind so viele!“

    Über der REST-API befindet sich jedoch eine Konvention, die dieses Problem löst.

    Listenanfrage


    Um die API erweiterbar zu machen, können Sie die GET-Parameter sofort für die Paginierung verwenden: die Größe der aktuellen Seite und ihre Nummer, sodass genau der Teil der Seite, den wir angefordert haben, an uns zurückgegeben wird. Das ist bequem. Als Antwort können Sie nicht sofort ein Array angeben, sondern eine zusätzliche Verschachtelung hinzufügen. Beispielsweise enthält der Datenschlüssel ein Array, die von uns angeforderten Daten und der Metaschlüssel, der zuvor nicht vorhanden war, die Summe. Auf diese Weise kann die API zusätzliche Informationen zurückgeben. Zusätzlich zur Zählung gibt es möglicherweise noch einige andere Informationen - sie sind erweiterbar. Wenn ein Junior es nicht sofort tat und erst nachdem er gebeten wurde, eine Pyjinisierung durchzuführen, nahm er  eine inkompatible Änderung vor , brach die API und alle Kunden mussten sie wiederholen - normalerweise tut es sehr weh.

    01.  GET /articles?page[size]=30&page[number]=2
    02.  Content-Type: application/json

    01.  HTTP/1.1 200 OK
    02.  {
    03.    "data": [{ "id": 1, "title": "JSONAPI"}, ...],
    04.    "meta": { "count": 10080 }
    05.  }



    Die Pajinisierung ist anders. Ich biete verschiedene Life-Hacks an, die Sie verwenden können.

    [Offset] ... [Limit]


    01.  GET /articles?page[offset]=30&page[limit]=30
    02.  Content-Type: application/json

    01.  HTTP/1.1 200 OK
    02.  {
    03.    "data": [{ "id": 1, "title": "JSONAPI"}, ...],
    04.    "meta": { "count": 10080 }
    05.  }

    Diejenigen, die mit Datenbanken arbeiten, haben möglicherweise bereits einen Subcortex [offset] ... [limit]. Die Verwendung anstelle von Seite [Größe] ... Seite [Anzahl] wird einfacher. Dies ist ein etwas anderer Ansatz.

    Cursor positionieren


    01. GET /articles?page[published_at]=1538332156
    02. Content-Type: application/json


    01. HTTP/1.1 200 OK
    02. {
    03.     "data": [{ "id": 1, "title": "JSONAPI"}, ...],
    04.     "meta": { "count": 10080 }
    05. }


    Die Cursorposition verwendet einen Zeiger auf die Entität, mit der das Laden von Datensätzen gestartet werden soll. Zum Beispiel ist es sehr praktisch, wenn Sie Paginierung oder Laden in Listen verwenden, die sich häufig ändern. Nehmen wir an, in unserem Blog werden ständig neue Artikel geschrieben. Die dritte Seite ist jetzt nicht dieselbe dritte Seite wie in einer Minute, aber wenn wir zur vierten Seite gehen, erhalten wir einige Datensätze von der dritten Seite, da die gesamte Liste verschoben wird.

    Dieses Problem wird durch die Cursor-Paginierung gelöst. Wir sagen: "Lade die Artikel, die nach dem damals veröffentlichten Artikel kommen" - rein technisch kann es keine Verschiebung mehr geben, und das ist cool.

    Problem N +1


    Das nächste Problem, auf das unser Junior-Entwickler definitiv stoßen wird, ist das N + 1-Problem (Backender werden es verstehen). Angenommen, Sie möchten eine Liste von 10 Artikeln auflisten. Wir laden eine Liste von Artikeln hoch, jeder Artikel hat einen Autor und für jeden muss ein Autor heruntergeladen werden. Wir versenden:

    • 1 Anforderung einer Artikelliste;
    • 10 Anfragen an die Autoren eines jeden Artikels.

    Total: 11 Abfragen, um eine kleine Liste anzuzeigen.

    Links hinzufügen


    Auf dem Backend ist dieses Problem in allen ORMs gelöst - denken Sie daran, diese Verbindung hinzuzufügen. Diese Anschlüsse können auch am Frontend verwendet werden. Dies geschieht wie folgt: Sie können einen speziellen GET-Parameter mit dem Namen include (wie im Backend) verwenden, der angibt, welche Links zusammen mit den Artikeln geladen werden müssen. Angenommen, wir laden Artikel hoch und möchten den Autor sofort zusammen mit den Artikeln ermitteln. Die Antwort lautet: Eigene Artikelattribute werden in Daten übertragen und der Beziehungsschlüssel hinzugefügt. In diesen Schlüssel setzen wir alle Verbindungen. Somit haben wir in einer Anfrage alle Daten erhalten, die zuvor 11 Anfragen erhalten haben. Dies ist ein cooler Life-Hack, der das Problem mit N + 1 am Frontend gut löst.

    01.  GET /articles?include =author
    02.  Content-Type: application/json



    01. НТТР/1.1 200 ОК
    02. { "data": [{
    03.   { attributes: { "id": 1, "title": "JSON API" },
    04.   { relationships: {
    05.    "author": { "id": 1, "name": "Avdeev" } }
    06.   }, ...
    07. }]}




    Das Problem der Datenvervielfältigung


    Angenommen, Sie möchten 10 Artikel anzeigen, in denen der Autor angegeben ist. Alle Artikel haben einen Autor, aber das Objekt mit dem Autor ist sehr groß (z. B. ein sehr langer Nachname, der ein Megabyte benötigt). Ein Autor ist 10 Mal in der Antwort enthalten, und 10 Einschlüsse desselben Autors in der Antwort benötigen 10 MB.

    Da alle Objekte gleich sind, wird das Problem, dass ein Autor 10 Mal (10 MB) enthalten ist, mit Hilfe der Normalisierung gelöst, die in Datenbanken verwendet wird. Am Frontend können Sie auch die Normalisierung für die Arbeit mit der API verwenden - das ist sehr cool.

    01. НТТР/1.1 200 ОК
    02. { "data": [{
    03.  "id": "1″, "type": "article",
    04.  "attributes": { "title": "JSON API" },
    05.  "relationships": { ... }
    06.   "author": { "id": 1, "type": "people" } }
    07. }, ... ]
    08. }


    Wir markieren alle Entitäten mit einem Typ (dies ist ein Repräsentationstyp, ein Ressourcentyp). Roy Fielding stellte das Konzept einer Ressource vor, dh sie forderten Artikel an - erhielten einen „Artikel“. In Beziehungen stellen wir eine Verknüpfung zu den Personen her, d. H., Wir haben die Personenressource noch an einem anderen Ort. Und wir nehmen die Ressource selbst in einen separaten enthaltenen Schlüssel auf, der auf derselben Ebene wie Daten liegt. Somit fallen alle verbundenen Entitäten in einer einzelnen Instanz in den enthaltenen Sonderschlüssel. Wir speichern nur Links und die Entitäten selbst werden in aufgenommen gespeichert. Anforderungsgröße verringert. Dies ist ein Life-Hack, von dem das beginnende Back-End nichts weiß. Er wird später herausfinden, wann er die API brechen muss.

    01. НТТР/1.1 200 ОК
    02. {
    03. "data": [ ... ],
    04.  "included": [{
    05.   "id": 1, "type": "people",
    06.   "attributes": { "name": "Avdeev" }
    07. }]
    08. }






    Es werden nicht alle Ressourcenfelder benötigt


    Der folgende Life Hack kann angewendet werden, wenn nicht alle Ressourcenfelder benötigt werden. Dies geschieht mit einem speziellen GET-Parameter, der die zurückzugebenden Attribute durch Kommas getrennt auflistet. Der Artikel ist beispielsweise groß, und im Inhaltsfeld können sich Megabyte befinden, und wir müssen nur die Liste der Überschriften anzeigen - wir benötigen den Inhalt in der Antwort nicht. Wenn Sie beispielsweise auch das Veröffentlichungsdatum benötigen, können Sie ein durch Kommas getrenntes „Veröffentlichungsdatum“ eingeben. Als Antwort erhalten Sie zwei Felder mit Attributen. Dies ist eine Konvention, die als Anti-Bikeshedding-Tool verwendet werden kann.

    GET /articles?fields[article]=title НТТР/1.1

    01. НТТР/1.1 200 OK
    02. { "data": [{
    03.  "id": "1″, "type": "article",
    04.  "attributes": { "title": "Про JSON API" },
    05.   }, ... ]
    06. }




    Suche nach Artikeln


    Oft brauchen wir Suchanfragen und Filter. Hierfür gibt es Vereinbarungen - spezielle Filter GET-Parameter:

    ●  - Suche; ●  - Artikel von einem bestimmten Datum herunterladen; ●  - Artikel hochladen, die gerade veröffentlicht wurden; ●  - Artikel mit dem Erstautor herunterladen.GET /articles?filters[search]=api HTTP/1.1
    GET /articles?fiIters[from_date]=1538332156 HTTP/1.1
    GET /articles?filters[is_published]=true HTTP/1.1
    GET /articles?fiIters[author]=1 HTTP/1.1

    Artikel sortieren


    ●  - nach Titel; ●  - nach Veröffentlichungsdatum; ●  - bis zum Veröffentlichungsdatum in umgekehrter Richtung; ●  - zuerst nach Autor, dann nach Erscheinungsdatum in umgekehrter Richtung, wenn der Artikel vom selben Autor stammt.GET /articles?sort=title НТТР/1.1
    GET /articles?sort=published_at HTTP/1.1
    GET /articles?sort=-published_at HTTP/1.1
    GET /articles?sort=author,-publisbed_at HTTP/1.1

    URLs müssen geändert werden


    Lösung: Hypermedien, die ich bereits erwähnt habe, können wie folgt durchgeführt werden. Wenn das Objekt (die Ressource) selbstbeschreibend sein soll, der Client mithilfe von Hypermedien verstehen kann, was damit getan werden kann, und der Server unabhängig vom Client entwickelt werden kann, können Sie der Artikelliste Links hinzufügen, indem Sie spezielle Links zum Artikel selbst verwenden : Oder verwandt, wenn wir dem Kunden mitteilen möchten, wie ein Kommentar zu diesem Artikel hochgeladen werden soll: Der Kunde erkennt, dass ein Link vorhanden ist, klickt darauf und lädt den Kommentar herunter. Wenn es keinen Link gibt, gibt es keine Kommentare. Das ist praktisch, aber so wenige tun es. Fielding entwickelte die Prinzipien von REST, aber nicht alle kamen in unsere Branche. Wir verwenden hauptsächlich zwei oder drei.

    01. GET /articles НТТР/1.1
    02. {
    03.  "data": [{
    04.   ...
    05.   "links": { "self": "http://localhost/articles/1"
    },
    06.   "relationships": { ... }
    07.  }],
    08.  "links": { "self": "http://localhost/articles" }
    09. }



    01. ...
    02. "relationships": {
    03.  "comments": {
    04.   "links": {
    05.   "self": "http://localhost/articles/l/relationships/comments
    ",
    06.   "related": "http://localhost/articles/l/comments"
    07.   }
    08.  }
    09. }



    Im Jahr 2013 hat Steve Klabnik all die Life-Hacks, von denen ich Ihnen erzählt habe, in die JSON-API-Spezifikation aufgenommen und sich zusätzlich zu JSON als neuer Medientyp registriert . So kam unser Junior-Backend-Entwickler, der sich allmählich weiterentwickelte, zur JSON-API.

    JSON-API


    Alles wird auf der Website http://jsonapi.org/implementations/ ausführlich beschrieben: Es gibt sogar eine Liste von 170 verschiedenen Implementierungen von Spezifikationen für 32 Programmiersprachen - und diese werden nur dem Katalog hinzugefügt. Bibliotheken, Parser, Serializer usw. wurden bereits geschrieben.

    Da diese Spezifikation Open Source ist, investiert jeder in sie. Ich selbst habe etwas geschrieben. Ich bin sicher, dass es viele solche Leute gibt. Sie können sich diesem Projekt selbst anschließen.

    JSON API Pros


    Die JSON-API-Spezifikation löst eine Reihe von Problemen - eine gemeinsame Vereinbarung für alle . Da es eine allgemeine Vereinbarung gibt, streiten wir  uns nicht im Team  - der Fahrradschuppen ist dokumentiert. Wir haben eine Vereinbarung getroffen, aus welchen Materialien ein Fahrradschuppen hergestellt und wie er lackiert werden soll.

    Wenn der Entwickler nun etwas falsch macht und ich es sehe, starte ich die Diskussion nicht, sondern sage: "Nicht gemäß der JSON-API!" Und zeige es in der Spezifikation. Sie hassen mich in der Firma, gewöhnen sich aber allmählich daran, und jeder begann, die JSON-API zu mögen. Wir machen neue Standarddienste gemäß dieser Spezifikation. Wir haben einen Datumsschlüssel, wir sind bereit, Meta hinzuzufügen, Schlüssel einzuschließen. Für Filter gibt es einen reservierten GET-Parameterfilter. Wir streiten uns nicht darüber, wie man einen Filter nennt - wir verwenden diese Spezifikation. Hier wird beschrieben, wie Sie eine URL erstellen.

    Da wir nicht streiten, sondern geschäftliche Aufgaben erledigen, ist die Entwicklungsproduktivität höher . Wir haben die Spezifikationen beschrieben, der Entwickler das Backend gelesen, die API erstellt, wir haben es angeschraubt - der Kunde ist zufrieden.

    Populäre Probleme wurden zum Beispiel durch Paginierung bereits gelöst . Es gibt viele Hinweise in der Spezifikation.

    Da es sich um JSON handelt (dank Douglas Crockford für dieses Format), ist es übersichtlicher als XML. Es ist recht einfach zu lesen und zu verstehen .

    Die Tatsache, dass dies Open Source ist, kann sowohl ein Plus als auch ein Minus sein, aber ich liebe Open Source.

    Nachteile JSON API


    Das Objekt ist gewachsen (Datum, Attribute, eingeschlossen usw.) - das Frontend muss die Antworten analysieren: Sie müssen in der Lage sein, über Arrays zu iterieren, das Objekt zu umgehen und zu wissen, wie Reduce funktioniert. Nicht alle unerfahrenen Entwickler kennen diese komplexen Dinge. Es gibt Bibliotheken von Serialisierern / Deserialisierern, die Sie verwenden können. Im Allgemeinen funktioniert dies nur mit Daten, aber die Objekte sind groß.

    Und das  Backend hat Schmerzen:

    • Nesting Control - Include kann sehr weit geklettert werden;
    • Die Komplexität von Datenbankabfragen - sie werden manchmal automatisch erstellt und sind sehr schwierig.
    • Sicherheit - Sie können in den Dschungel klettern, besonders wenn Sie eine Art Bibliothek anschließen.
    • Die Spezifikation ist schwer zu lesen. Sie ist auf Englisch, und es erschreckte einige, aber nach und nach gewöhnten sich alle daran.
    • Nicht alle Bibliotheken implementieren die Spezifikation gut - dies ist ein Open Source-Problem.

    Fallstricke der JSON-API


    Ein bisschen Hardcore.

    Die Anzahl der Beziehungen in der Ausgabe ist nicht begrenzt. Wenn wir Artikel aufnehmen, anfordern und ihnen Kommentare hinzufügen, erhalten wir als Antwort alle Kommentare zu diesem Artikel. Es gibt 10.000 Kommentare - alle 10.000 Kommentare bekommen: So kamen wirklich 5 MB zu unserer Anfrage: „Es steht in der Spezifikation - wir müssen die Anfrage richtig umformulieren: Wir fordern Kommentare mit einem Filter nach Artikel an, wir sagen:„ 30 Stück, bitte "Und 30 Kommentare bekommen. Das ist Mehrdeutigkeit. Die gleichen Dinge können mehrdeutig formuliert werden : ●  - Wir bitten um einen Artikel mit Kommentaren; ●  - Kommentare zum Artikel anfordern; ●

    GET /articles/1?include=comments НТТР/1.1

    01. ...
    02. "relationships": {
    03.  "comments": {
    04.   "data": [0 ... ∞]
    05.  }
    06. }




    GET /comments?filters[article]=1&page[size]=30 HTTP/1.1

    01. {
    02. "data": [0 ... 29]
    03. }






    GET /articles/1?include=comments HTTP/1.1
    GET /articles/1/comments HTTP/1.1
    GET /comments?filters[article]=1 HTTP/1.1 - Kommentare mit einem Filter nach Artikel anfordern.

    Dies ist ein und dasselbe - die gleichen Daten, die auf unterschiedliche Weise erhalten werden, gibt es einige Unklarheiten. Diese Falle ist nicht sofort sichtbar.

    Eins-zu-viele polymorphe Verbindungen schleichen sich sehr schnell in REST ein. Es gibt eine bemerkenswerte polymorphe Verbindung im Backend, die sich in REST ausbreitet. So sollte es passieren, aber es kann verkleidet werden. Sie können die JSON-API nicht maskieren - sie wird angezeigt. Komplexe Viele-zu-Viele-Beziehungen mit erweiterten Optionen . Außerdem werden alle Verknüpfungstabellen ausgegeben:

    01. GET /comments?include=commentable НТТР/1.1
    02.
    03. ...
    04. "relationships": {
    05.  "commentable": {
    06.   "data": { "type": "article", "id": "1″ }
    07. }
    08. }






    01. GET /users?include=users_comments НТТР/1.1
    02.
    03. ...
    04. "relationships": {
    05.  "users_comments": {
    06.   "data": [{ "type": "users_comments", "id": "1″ }, ...]
    07.  },
    08. }


    Prahlerei


    Swagger ist ein Online-Dokumentationswerkzeug.

    Nehmen wir an, unser Backend-Entwickler wurde gebeten, Dokumentation für seine API zu schreiben, und er hat sie geschrieben. Dies ist einfach, wenn die API einfach ist. Wenn es sich um eine JSON-API handelt, kann Swagger nicht so einfach geschrieben werden.

    Beispiel: Swagger Zoohandlung. Jede Methode kann geöffnet werden, siehe Antwort und Beispiele.



    Dies ist ein Beispiel für ein Haustiermodell. Hier ist eine coole Oberfläche, alles ist leicht zu lesen.



    Und so sieht die Erstellung des JSON-API-Modells aus:



    Das ist nicht so toll. Wir brauchen Daten, in Daten etwas mit Beziehungen, enthalten sind 5 Arten von Modellen usw. Sie können Swagger schreiben, die Open API ist eine mächtige Sache, aber kompliziert.

    Alternative


    Es gibt eine OData-Spezifikation, die etwas später erschien - im Jahr 2015. Dies ist "Der beste Weg zur Erholung", wie die offizielle Website versichert. Es sieht folgendermaßen aus:

    01. GET http://services.odata.org/v4/TripRW/People HTTP/1.1- GET request;
    02. OData-Version: 4.0 - Sondertitel mit Fassung;
    03. OData-MaxVersion: 4.0 - der zweite spezielle Header mit der Version Die

    Antwort sieht so aus: Hier ist die erweiterte Anwendung / json und das Objekt. Erstens haben wir OData nicht verwendet, da es das gleiche wie die JSON-API ist, aber es ist nicht präzise. Es gibt riesige Objekte und es scheint mir, dass alles viel schlechter gelesen wird. OData wurde auch in Open Source veröffentlicht, ist aber komplizierter.

    01. HTTP/1.1 200 OK
    02. Content-Type: application/json; odata.metadata=minimal
    03. OData-Version: 4.0
    04. {
    05.  ’@odata.context’:  ’http://services.odata.org/V4/
    06.  ’@odata.nextLink’  : ’http://services.odata.org/V4/
    07.  ’value’: [{
    08.   ’@odata.etag’: 1W/108D1D5BD423E51581′,
    09.   ’UserName’: ’russellwhyte’,
    10.   ...





    Was ist mit GraphQL?


    Als wir nach einem neuen API-Format suchten, stießen wir natürlich auf diesen Hype.

    Hohe Eintrittsschwelle.

    Aus Sicht des Frontends sieht alles cool aus, aber Sie können den neuen Entwickler nicht dazu bringen, GraphQL zu schreiben, da Sie es zuerst studieren müssen. Es ist wie bei SQL - Sie können SQL nicht sofort schreiben, Sie müssen zumindest lesen, was es ist, die Tutorials durchgehen, dh die Eintrittsschwelle erhöht sich.

    Die Wirkung des Urknalls.

    Wenn es im Projekt keine API gab und wir damit begannen, GraphQL zu verwenden, stellten wir nach einem Monat fest, dass es nicht zu uns passt, es wird zu spät sein. Ich muss Krücken schreiben. Sie können sich mit der JSON-API oder OData weiterentwickeln - das einfachste RESTful, das sich schrittweise verbessert, wird zu einer JSON-API.

    Hölle im Backend.

    GraphQL ruft die Hölle im Backend auf - eins zu eins, genau wie die vollständig implementierte JSON-API, da GraphQL die vollständige Kontrolle über die Abfragen erhält und dies eine Bibliothek ist und Sie eine Reihe von Fragen lösen müssen:

    • Verschachtelungskontrolle;
    • Rekursion
    • Frequenzbegrenzung;
    • Zugangskontrolle.

    Anstelle von Schlussfolgerungen


    Ich empfehle, nicht mehr über den Fahrradschuppen zu streiten, sondern das Anti-Bikeshedding-Tool als Spezifikation zu verwenden und einfach eine API mit einer guten Spezifikation zu erstellen.

    Um Ihren Standard zur Lösung des Problems eines Fahrradschuppens zu finden, können Sie folgende Links aufrufen :

    http://jsonapi.org
    http://www.odata.org
    https://graphgl.org
    http: //xmlrpc.scripting. com
    https://www.jsonrpc.org

    Kontakte des Sprechers Alexei Avdeev: alexey-avdeev.com und Profil auf  github .

    Kollegen, wir haben  den Empfang von Berichten im  Frontend Conf eröffnet , das am 27. und 28. Mai im Rahmen von RIT ++ stattfinden wird . Unser Programmkomitee hat begonnen, in den nächsten drei Monaten ein cooles Programm zusammenzustellen.

    Hast du eine Geschichte zu erzählen? Möchten Sie Ihre Erfahrungen mit der Community teilen? Kann Ihr Bericht das Leben vieler Front-End-Unternehmen verbessern? Sind Sie ein Experte in einem engen, aber wichtigen Thema und möchten Ihr Wissen teilen? Bewerbung einreichen !

    Verfolgen Sie die Vorbereitungen durch den Newsletter und schreiben Sie in den Kommentaren zu dem Artikel Ideen darüber, wer eingeladen werden sollte und über welches Thema gesprochen werden soll.

    Jetzt auch beliebt: