Eine Webanwendung als REST-API-Client: Umgang mit Ressourcen-IDs

21

Verschiedene Konzepte im Zusammenhang mit REST-Konflikten treten in meinem Kopf auf, wenn ich versuche, sie zu implementieren.

Ich habe ein REST-volles Back-End-API-System, das die Geschäftslogik enthält, und eine Webanwendung, die die Benutzeroberfläche bereitstellt. Aus verschiedenen Quellen zu REST (insbesondere REST in der Praxis: Hypermedien und Systemarchitektur ) weiß ich, dass ich keine unformatierten Bezeichner meiner Entitäten offen legen, sondern Hyperlinks mit zurückgeben sollte rel="self".

Betrachten Sie das Beispiel. Die REST-API verfügt über eine Ressource, die eine Person zurückgibt:

<Person>
  <Links>
    <Link rel="self" href="http://my.rest.api/api/person/1234"/>
  </Links>
  <Pets>
    <Link rel="pet" href="http://my.rest.api/api/pet/678"/>
  </Pets>
</Person>

Das Problem tritt bei der Webanwendung auf. Angenommen, es wird eine Seite zurückgegeben, die einen Hyperlink zu Browsern enthält:

<body class="person">
  <p>
    <a href="http://my.web.app/pet/???????" />
  </p>
</body>

Was soll ich in das hrefAttribut setzen? Wie behalte ich die URL der API-Entität in der Webanwendung, um die Entität abzurufen, wenn ein Benutzer die Zielseite öffnet?

Die Anforderungen scheinen sich zu widersprechen:

  1. Der Hyperlink hrefsollte zur Webanwendung führen, da dies das System ist, auf dem die Benutzeroberfläche gehostet wird
  2. Die hrefsollte eine ID der Entität haben, da die Web-App in der Lage sein muss, die Entität zu adressieren, wenn die Zielseite geöffnet wird
  3. Die Web-App sollte keine REST-URLs parsen / konstruieren, da diese nicht REST-voll sind, heißt es in dem genannten Buch

URIs sollten für Verbraucher undurchsichtig sein. Nur der Emittent des URI kann ihn interpretieren und einer Ressource zuordnen.

Ich kann also nicht einfach 1234die API-Antwort-URL auslesen, da ich sie als RESTful-Client so behandeln sollte, als wäre sie so etwas wie http://my.rest.api/api/AGRIDd~ryPQZ^$RjEL0j. Andererseits muss ich eine URL angeben, die zu meiner Web-App führt und ausreicht, damit die App die ursprüngliche URL der API wiederherstellt und diese URL für den Zugriff auf die API-Ressourcen verwendet.

Am einfachsten ist es wahrscheinlich, die API-URLs von Ressourcen als Zeichenfolgen-IDs zu verwenden. Aber Webseiten-URLs wie http://my.web.app/person/http%3A%2F%2Fmy.rest.api%2Fapi%2Fperson%2F1234sind hässlich.

Für eine Desktop-App oder eine Single-Page-Javascript-App scheint alles ganz einfach zu sein. Da sie ununterbrochen leben, können sie die URLs nur zusammen mit den Serviceobjekten für die Anwendungslebensdauer im Speicher behalten und sie bei Bedarf verwenden.

Mit einer Web-App kann ich mir mehrere Ansätze vorstellen, aber alle scheinen komisch:

  1. Ersetzen Sie den Host in den API-URLs und behalten Sie nur das Ergebnis bei. Der große Nachteil ist, dass die Webanwendung die URL verarbeiten muss, die von der API generiert wird, was eine monströse Kopplung bedeutet. Darüber hinaus ist es nicht wieder RESTful, weil meine Web-App beginnt, die URLs zu interpretieren.
  2. Stellen Sie die Roh-IDs in der REST-API zusammen mit den Links bereit, erstellen Sie daraus die URLs der Webanwendung und ermitteln Sie anhand der IDs auf dem Webanwendungsserver die erforderlichen Ressourcen in der API. Dies ist besser, wirkt sich jedoch auf die Leistung des Webanwendungsservers aus, da die Webanwendung die REST-Dienstnavigation durchlaufen muss, um eine Kette von Get-by-ID-Anforderungen in irgendeiner Form auszugeben, um alle Anforderungen eines Browsers zu verarbeiten. Für eine etwas verschachtelte Ressource kann dies kostspielig sein.
  3. Speichern Sie alle selfvom API zurückgegebenen URLs in einer permanenten Zuordnung (DB?) Auf dem Webanwendungsserver. Generieren Sie einige IDs für diese, erstellen Sie mithilfe der IDs die URLs der Webanwendungsseite und rufen Sie die URLs der REST-Serviceressourcen ab. Dh ich behalte die http://my.rest.api/pet/678URL irgendwo mit einem neuen Schlüssel, z. B. 3und generiere die Webseiten-URL als http://my.web.app/pet/3. Dies sieht aus wie eine Art HTTP-Cache-Implementierung. Ich weiß nicht warum, aber es kommt mir komisch vor.

Oder bedeutet dies alles, dass RESTful-APIs nicht als Back-End für Webanwendungen dienen können?

Pavel Gatilov
quelle
1
Es ist unklar, was Sie erreichen möchten, wahrscheinlich weil Ihre einfache Absicht unter den Schichten der Architektur liegt, die Sie aufeinander aufbauen. Es ist daher schwer zu sagen, ob "RESTful APIs" Ihnen wirklich helfen würden. Nach meinem Verständnis Ihres Problems ist Option 2 eine einfache und praktikable Lösung. Das "Problem" ist hier "RESTful APIs" inhärent. RestIsJustSqlReinvented und Sie werden tatsächlich auf dasselbe Problem stoßen, wenn Sie versuchen, einen ausreichend komplexen Subgraphen aus einem beliebigen RDBMS abzurufen. Verwenden Sie einen Cache oder eine Darstellung, die für Ihre Abfragen optimiert ist.
back2dos

Antworten:

5

Bearbeitet, um Fragenaktualisierungen zu adressieren, vorherige Antwort entfernt

Wenn ich mir Ihre Änderungen an Ihrer Frage ansehe, denke ich, dass ich das Problem, mit dem Sie konfrontiert sind, besser verstehe. Da es in Ihren Ressourcen kein Feld gibt, das eine Kennung darstellt (nur ein Link), können Sie in Ihrer GUI nicht auf diese bestimmte Ressource verweisen (dh auf einen Link zu einer Seite, die ein bestimmtes Haustier beschreibt).

Als Erstes muss festgestellt werden, ob ein Haustier ohne Besitzer überhaupt einen Sinn ergibt. Wenn wir ein Haustier ohne Besitzer haben können, dann würde ich sagen, wir brauchen eine Art einzigartiges Eigentum an dem Haustier, mit dem wir uns darauf beziehen können. Ich glaube nicht, dass dies die Nichtveröffentlichung der ID verletzen würde, da die tatsächliche Ressourcen-ID immer noch in einem Link versteckt ist, den der REST-Client nicht analysieren würde. In diesem Sinne könnte unsere Haustierressource so aussehen:

<Entity type="Pet">
    <Link rel="self" href="http://example.com/pets/1" />
    <Link rel="owner" href="http://example.com/people/1" />
    <UniqueName>Spot</UniqueName>
</Entity>

Wir können jetzt den Namen dieses Begleiters von Spot auf Fido aktualisieren, ohne in der gesamten Anwendung mit den tatsächlichen Ressourcen-IDs herumspielen zu müssen. Ebenso können wir auf dieses Haustier in unserer GUI mit etwas verweisen, wie:

http://example.com/GUI/pets/Spot

Wenn das Haustier ohne einen Besitzer keinen Sinn ergibt (oder Haustiere im System ohne einen Besitzer nicht erlaubt sind), können wir den Besitzer als Teil der "Identität" des Haustieres im System verwenden:

http://example.com/GUI/owners/John/pets/1 (erstes Haustier in der Liste für John)

Eine kleine Anmerkung, wenn sowohl Haustiere als auch Menschen getrennt voneinander existieren können, würde ich den Einstiegspunkt für die API nicht zur Ressource "Menschen" machen. Stattdessen würde ich eine allgemeinere Ressource erstellen, die einen Link zu Personen und Haustieren enthält. Es könnte eine Ressource zurückgeben, die wie folgt aussieht:

<Entity type="ResourceList">
    <Link rel="people" href="http://example.com/api/people" />
    <Link rel="pets" href="http://example.com/api/pets" />
</Entity>

Wenn Sie also nur den ersten Einstiegspunkt in die API kennen und keine der URLs verarbeiten, um die System-IDs herauszufinden, können Sie Folgendes tun:

Der Benutzer meldet sich bei der Anwendung an. Der REST-Client greift auf die gesamte Liste der verfügbaren Personenressourcen zu, die wie folgt aussehen können:

<Entity type="Person">
    <Link rel="self" href="http://example.com/api/people/1" />
    <Pets>
        <Link rel="pet" href="http://example.com/api/pets/1" />
        <Link rel="pet" href="http://example.com/api/pets/2" />
    </Pets>
    <UniqueName>John</UniqueName>
</Entity>
<Entity type="Person">
    <Link rel="self" href="http://example.com/api/people/2" />
    <Pets>
        <Link rel="pet" href="http://example.com/api/pets/3" />
    </Pets>
    <UniqueName>Jane</UniqueName>
</Entity>

Die GUI durchläuft jede Ressource und druckt ein Listenelement für jede Person aus, wobei der eindeutige Name als "ID" verwendet wird:

<a href="http://example.com/gui/people/1">John</a>
<a href="http://example.com/gui/people/2">Jane</a>

Dabei könnte es auch jeden gefundenen Link mit einem rel von "pet" verarbeiten und die pet-Ressource wie folgt abrufen:

<Entity type="Pet">
    <Link rel="self" href="http://example.com/api/pets/1" />
    <Link rel="owner" href="http://example.com/api/people/1" />
    <UniqueName>Spot</UniqueName>
</Entity>

Hiermit kann ein Link wie folgt gedruckt werden:

<!-- Assumes that a pet can exist without an owner -->
<a href="http://example.com/gui/pets/Spot">Spot</a>

oder

<!-- Assumes that a pet MUST have an owner -->
<a href="http://example.com/gui/people/John/pets/Spot">Spot</a>

Wenn wir mit dem ersten Link fortfahren und davon ausgehen, dass unsere Eintragsressource einen Link mit einer Beziehung von "Haustieren" enthält, würde der Kontrollfluss in der GUI ungefähr so ​​aussehen:

  1. Die Seite wird geöffnet und der Pet Spot angefordert.
  2. Laden Sie die Ressourcenliste vom API-Einstiegspunkt.
  3. Laden Sie die Ressource, die mit dem Begriff "Haustiere" zusammenhängt.
  4. Durchsuchen Sie jede Ressource in der Antwort "Haustiere" und finden Sie eine, die mit Spot übereinstimmt.
  5. Informationen zum Spot anzeigen.

Die Verwendung des zweiten Links wäre eine ähnliche Ereigniskette, mit der Ausnahme, dass People der Einstiegspunkt in die API ist und wir zuerst eine Liste aller Personen im System erhalten, die übereinstimmende finden und dann alle dazugehörigen Haustiere finden Gehen Sie zu dieser Person (verwenden Sie erneut das rel-Tag) und suchen Sie die Person mit dem Namen Spot, damit wir die entsprechenden Informationen anzeigen können.

Mike
quelle
Danke, Mike. Ich habe meine Frage aktualisiert, um sie etwas klarer zu gestalten. Das Problem mit Ihrer Antwort ist, dass ich nicht zustimmen kann, dass ein REST-Client URLs analysieren kann. Wenn dies der Fall ist, wird es an die URLs gekoppelt. Dies verstößt gegen eine der Kernideen von REST: Die Clients sollten rels zum Auswählen der Links verwenden, jedoch keine Kenntnis der URL-Struktur voraussetzen. REST behauptet, dass es einer API freigestellt ist, URLs nach Belieben zu ändern, sofern diese relunverändert bleiben. Durch das Parsen von URLs kommen wir SOAP näher als REST.
Pavel Gatilov
Danke nochmal. Sie haben unseren bisherigen Ansatz beschrieben. In gewisser Weise legen wir Identifikatoren offen. Das Einzige ist, dass wir versuchen, natürliche Identifikatoren freizulegen, wann immer dies möglich ist.
Pavel Gatilov
6

Bedeutet das alles, dass RESTful-APIs nicht als Back-End für Webanwendungen dienen können?

Ich frage mich, ob es sich lohnt, zwischen einer REST-API und einer Webanwendung zu unterscheiden. Ihre „Web - Anwendung“ sollte nur alternative sein (HTML) Darstellungen der gleichen Ressourcen - das heißt, ich verstehe nicht , wie oder warum Sie erwarten , den Zugang http://my.rest.api/...und http://my.web.app/...und dass sie sein , gleichzeitig gleich und verschieden.

Ihr "Client" ist in diesem Fall der Browser und versteht HTML und JavaScript. Das ist meiner Meinung nach die Webanwendung. Jetzt können Sie anderer Meinung sein und denken, dass Sie über foo.com auf diese Webanwendung zugreifen und alles andere über api.foo.com verfügbar machen - aber Sie müssen sich dann fragen, wie foo.com mir die Darstellung der Ressource zur Verfügung gestellt hat. Das "Back-End" von foo.com ist in der Lage zu verstehen, wie Ressourcen von api.foo.com gefunden werden. Ihre Webanwendung ist lediglich ein Proxy geworden - nicht anders, als wenn Sie alle zusammen mit einer anderen API (von einer anderen Person) gesprochen hätten.

Ihre Frage kann also verallgemeinert werden zu: "Wie kann ich Ressourcen mit meinen eigenen URIs beschreiben, die in anderen Systemen vorhanden sind?" Das ist trivial, wenn man bedenkt, dass nicht der Client (HTML / JavaScript), sondern der Server verstehen muss, wie das geht. Wenn Sie meiner ersten Herausforderung zustimmen, können Sie sich Ihre Webanwendung einfach als separate REST-API vorstellen, die eine andere REST-API überträgt oder an diese delegiert.

Wenn Ihr Client darauf zugreift, muss my.web.app/pets/1er die Pet-Schnittstelle präsentieren, da dies entweder von der serverseitigen Vorlage zurückgegeben wurde oder wenn es sich um eine asynchrone Anforderung für eine andere Darstellung (z. B. JSON oder XML) handelt, wird dies im Content-Type-Header angezeigt .

Der Server, der dies bereitstellt, ist dafür verantwortlich, zu verstehen, was ein Haustier ist und wie ein Haustier auf dem fernen System erkannt wird. Wie Sie dies tun, bleibt Ihnen überlassen - Sie können einfach die ID nehmen und einen anderen URI generieren, was Sie für unangemessen halten, oder Sie können über eine eigene Datenbank verfügen, in der der Remote-URI gespeichert ist und die Anforderung als Proxy fungiert. Das Speichern dieser URI ist in Ordnung - es entspricht einem Lesezeichen. Sie würden dies alles nur tun, um einen separaten Domainnamen zu haben. Ich weiß ehrlich gesagt nicht, warum Sie das wollen - Ihre REST-API-URIs müssen auch lesezeichenfähig sein.

Sie haben das meiste bereits in Ihrer Frage angesprochen, aber ich glaube, Sie haben es auf eine Weise umrahmt, die wirklich nicht anerkannt hat, dass es die praktische Art ist, das zu tun, was Sie tun möchten (basierend auf dem, was ich für richtig halte) willkürliche Einschränkung - dass die API und die Anwendung getrennt sind). Wenn Sie die Frage stellen, ob REST-APIs kein Back-End für Webanwendungen sein können, und wenn Sie darauf hinweisen, dass die Leistung ein Problem darstellt, konzentrieren Sie sich vermutlich auf die falschen Dinge. Es ist so, als ob Sie sagen, Sie können keinen Mashup erstellen. Es ist, als würde man sagen, dass das Web nicht funktioniert.

Doug
quelle
Ich erwarte nicht, dass die Web-App lediglich eine Darstellung für die API ist. Es kann viele Unterschiede geben, z. B. mehrere untergeordnete Ressourcen zusammen mit einer Stammressource auf einer Seite anzeigen. Ich möchte nicht, dass die Web-App-URLs die internen IDs des API-Datenspeichers enthalten, wenn Sie damit sagen, dass ich davon ausgehe, dass die beiden Systeme identisch sind. Es geht mir hier nicht um Leistung, es ist nicht das Problem. Die Frage ist eigentlich: "Wie kann ich 3 my.web.app/pets/3einfügen, ohne REST-API-URLs zu analysieren?"
Pavel Gatilov
Korrigieren meiner eigenen Neuphrase: 'Wie kann ich 3 my.web.app/pets/3eingeben, ohne die URL der entsprechenden REST-API-Ressource zu analysieren my.rest.api/v0/persons/2/pets/3? Oder was lege ich da hin? '
Pavel Gatilov
Ich denke, Sie verwechseln den Status des Clients mit den Darstellungen, die diesen Status bestimmen. Sie müssen nicht setzen 3in , app/pets/3weil app/pets/3undurchsichtig ist, es zeigt auf , was auch immer Ressource Ihres Web - App will. Wenn dies eine komponierte Ansicht mehrerer anderer Ressourcen ist (in anderen Systemen - Ihre API ist eine davon), müssen Sie die Hyperlinks zu diesen Systemen auf dem Web-App-Server speichern und sie dann abrufen und in ihre Darstellungen auflösen ( zB JSON oder XML) und dann als Teil Ihrer Antwort bereitstellen.
Doug
Stellen Sie es sich so vor - vergessen Sie Ihre API und App. Angenommen, Sie möchten eine Website erstellen, auf der Personen ihre bevorzugten Facebook- und Twitter-Beiträge sammeln können. Das sind entfernte Systeme. Sie werden nicht versuchen, die URIs über Ihre eigenen zu diesen Systemen zu tunneln oder zu kopieren. Sie würden eine "Board" -Ressource erstellen und es wäre Ihr Server, der weiß, dass dies darauf board/1hindeutet, facebook.com/post/123und twitter.com/status/789- wenn Sie eine Darstellung Ihres Boards bereitstellen, müssten Sie diese URIs in eine Darstellung auflösen, mit der Sie arbeiten können. Cache wo nötig.
Doug
Da Sie also möchten, dass sich Ihre API erheblich von Ihrer App unterscheidet (ich halte dies immer noch für fragwürdig), wird es nicht anders, wenn Sie sie wie ein Remote-System behandeln. Sie sagten, Leistung sei nicht das Problem, aber Sie sagten auch in Ihrer Frage, dass so etwas die Leistung beeinträchtigen würde.
Doug
5

Vorwort

Diese Antwort befasst sich speziell mit der Frage, wie Sie Ihr eigenes URL-Schema verwalten können, einschließlich eindeutiger lesezeichenfähiger URLs für Ressourcen, für die die Back-End-REST-API keinen Bezeichner explizit verfügbar macht, und ohne die von der API bereitgestellten URLs zu interpretieren.


Die Auffindbarkeit erfordert ein gewisses Maß an Wissen. Hier ist meine Sicht auf ein reales Szenario:

Angenommen, wir möchten eine Suchseite, auf der http://my.web.app/persondie Ergebnisse für jede Person einen Link zur Detailseite enthalten. Das einzige , was unser Front-End - Code muss wissen , um überhaupt etwas zu tun , ist die Basis - URL für seine REST - Datenquelle: http://my.rest.api/api. Die Antwort auf eine GET-Anforderung an diese URL lautet möglicherweise:

<Links>
    <Link ref="self" href="http://my.rest.api/api" />
    <Link rel="person" href="http://my.rest.api/api/person" />
    <Link rel="pet" href="http://my.rest.api/api/pet" />
</Links>

Da wir beabsichtigen, eine Liste von Personen anzuzeigen, senden wir als nächstes eine GETAnfrage über den personLink href an die href, die möglicherweise zurückgibt:

<Links>
    <Link ref="self" href="http://my.rest.api/api/person" />
    <Link rel="search" href="http://my.rest.api/api/person/search" />
</Links>

Wir möchten Suchergebnisse anzeigen, daher verwenden wir den Suchdienst, indem wir eine GETAnfrage an den searchLink href senden , die möglicherweise Folgendes zurückgibt:

<Persons>
    <Person>
        <Links>
            <Link rel="self" href="http://my.rest.api/api/person/1"/>
        </Links>
        <Pets>
            <Link rel="pet" href="http://my.rest.api/api/pet/10"/>
        </Pets>
    </Person>
    <Person>
        <Links>
            <Link rel="self" href="http://my.rest.api/api/person/2"/>
        </Links>
        <Pets>
            <Link rel="pet" href="http://my.rest.api/api/pet/20"/>
        </Pets>
    </Person>
</Persons>

Wir haben endlich unsere Ergebnisse, aber wie erstellen wir unsere Front-End-URLs?

Entfernen wir den Teil, den wir mit Sicherheit kennen: die API-Basis-URL, und verwenden Sie den Rest als Front-End-Kennung:

  • bekannte API-Basis: http://my.rest.api/api
  • angegebene URL für einzelne Entität: http://my.rest.api/api/person/1
  • Eindeutige ID: /person/1
  • Unsere Basis-URL: http://my.web.app
  • Unsere generierte Frontend-URL: http://my.web.app/person/1

Unsere Ergebnisse könnten so aussehen:

<ul>
    <li><a href="http://my.web.app/person/1">A person</a></li>
    <li><a href="http://my.web.app/person/2">A person</a></li>
</ul>

Wenn ein Benutzer diesem Front-End-Link zur Detailseite folgt, an welche URL senden wir die GETAnfrage nach Details zu diesem bestimmten Link person? Wir kennen unsere Methode zum Zuordnen von Back-End-URLs zu Front-End-URLs und kehren sie einfach um:

  • Front-End-URL: http://my.web.app/person/1
  • Unsere Basis-URL: http://my.web.app
  • Eindeutige ID: /person/1
  • bekannte API-Basis: http://my.rest.api/api
  • generierte API URL: http://my.rest.api/api/person/1

Wenn sich die REST-API dahingehend ändert, dass sich eine personURL jetzt befindet http://my.rest.api/api/different-person-base/person/1und bereits ein Lesezeichen vorhanden ist http://my.web.app/person/1, sollte die REST-API (zumindest zeitweise) Abwärtskompatibilität gewährleisten, indem sie auf die alte URL mit einer Umleitung auf die neue antwortet. Alle generierten Front-End-Links würden die neue Struktur automatisch enthalten.

Wie Sie wahrscheinlich bemerkt haben, müssen wir einige Dinge wissen, um in der API zu navigieren:

  • die API-Basis-URL
  • die personBeziehung
  • die searchBeziehung

Ich glaube nicht, dass daran etwas falsch ist. Wir gehen zu keinem Zeitpunkt von einer bestimmten URL-Struktur aus, sodass sich die Struktur der Entity-URL http://my.rest.api/api/person/1ändern könnte. Solange die API Abwärtskompatibilität bietet, funktioniert unser Code weiterhin.


Sie haben gefragt, wie unsere Routing-Logik den Unterschied zwischen zwei Front-End-URLs erkennen kann:

  • http://my.rest.api/api/person/1
  • http://my.rest.api/api/pet/3.

Zuerst möchte ich darauf hinweisen, dass Sie in Ihrem Kommentar die API-Basis verwendet haben, wenn wir in unserem Beispiel separate Basis-URLs für die Benutzeroberfläche und die REST-API verwenden. Ich werde das Beispiel mit separaten Basen fortsetzen, aber das Teilen einer Basis ist kein Problem. Wir können (oder sollten) in der Lage sein, UI-Routing-Methoden mithilfe des Medientyps aus dem Accept-Header der Anforderung zuzuordnen.

Bei der Weiterleitung zu einer bestimmten Detailseite können wir diese beiden URLs nicht unterscheiden, wenn wir streng darauf achten, dass keine Kenntnis über die Struktur der selfvon der API bereitgestellten URL (dh der undurchsichtigen Zeichenfolgen-ID) erlangt wird. Damit dies funktioniert, fügen wir in unsere Front-End-URLs einen weiteren unserer bekannten Informationen ein - den Entitätstyp, mit dem wir arbeiten.

Bisher hatten unsere Front-End-URLs das folgende Format: ${UI base}/${opaque string id}

Das neue Format könnte sein: ${UI base}/${entity type}/${opaque string id}

Mit dem /person/1Beispiel würden wir also enden mit http://my.web.app/person/person/1.

Mit diesem Format würde unsere UI-Routing-Logik funktionieren /person/person/1, und da wir wissen, dass das erste Token in der Zeichenfolge von uns selbst eingefügt wurde, können wir es herausziehen und darauf basierend zu der entsprechenden Detailseite (in diesem Beispiel Person) weiterleiten. Wenn Sie verlegen über diese URL sind, können wir etwas mehr hinzufügen. vielleicht: http://my.web.app/person/detail/person/1

In diesem Fall würden wir das /person/detailfor routing analysieren und den Rest als undurchsichtige Zeichenfolgen-ID verwenden.


Ich denke, dies führt zu einer extrem engen Kopplung der Web-App an die API.

Ich vermute, Sie meinen, da unsere generierte Front-End-URL einen Teil der API-URL enthält, benötigen wir eine Codeänderung, um die mit einem Lesezeichen versehene URL in die zu übersetzen, wenn sich die API-URL-Struktur ändert, ohne die alte Struktur zu unterstützen neue Version der API-URL. Mit anderen Worten, wenn die REST-API die ID einer Ressource (die undurchsichtige Zeichenfolge) ändert, können wir mit der alten ID nicht mit dem Server über diese Ressource sprechen. Ich denke nicht, dass wir eine Codeänderung in dieser Situation vermeiden können.

Was ist, wenn ich wollte, dass sich die URL-Struktur der Web-App von der der API unterscheidet?

Sie können eine beliebige URL-Struktur verwenden. Letztendlich muss eine lesezeichenfähige URL für eine bestimmte Ressource etwas enthalten, mit dem Sie eine API-URL abrufen können, die diese Ressource eindeutig identifiziert. Wenn Sie Ihren eigenen Bezeichner generieren und ihn mit der API-URL wie in Ansatz 3 zwischenspeichern, funktioniert dies so lange, bis jemand versucht, die mit einem Lesezeichen versehene URL zu verwenden, nachdem dieser Eintrag aus dem Cache gelöscht wurde.

Was passiert, wenn die Entitäten meiner Webanwendung nicht den API-Entitäten 1-1 zugeordnet wurden?

Die Antwort hängt von der Beziehung ab. In beiden Fällen benötigen Sie eine Möglichkeit, das Front-End API-URLs zuzuordnen.

Mike Partridge
quelle
Ich habe ein Problem mit diesem Ansatz. Es ist tatsächlich die Nummer 1 in meiner Liste der Lösungen. Was ich nicht verstehe, ist Folgendes: Wenn die Web-App die URLs nicht interpretiert und eindeutige IDs als undurchsichtige Zeichenfolgen (nur person/1, pet/3) behandelt, wie würde sie dann wissen, dass beim Öffnen eines Browsers die http://my.rest.api/api/person/1Benutzeroberfläche der Person angezeigt wird , und ob dies der Fall ist öffnet http://my.rest.api/api/pet/3sich dann pet UI?
Pavel Gatilov
Gute Frage! Ich habe die Antwort mit meiner Antwort aktualisiert.
Mike Partridge
Danke, Mike. Ich denke, dies führt zu einer extrem engen Kopplung der Web-App an die API. Was ist, wenn ich wollte, dass sich die URL-Struktur der Web-App von der der API unterscheidet? Was passiert, wenn die Entitäten meiner Webanwendung nicht den API-Entitäten 1-1 zugeordnet wurden? Ich denke immer noch, dass ich besser den Ansatz wählen sollte, einige Bezeichner offenzulegen, aber die Kunden auffordern, Links für die Navigation zu verwenden.
Pavel Gatilov
Dies ist ein interessantes Thema, daher hoffe ich, dass mir nichts entgeht. Ich habe meine Antwort mit Antworten auf Ihren Kommentar aktualisiert. Ich denke, einige Bezeichner aufzudecken, ist ein guter Kompromiss zwischen vollständiger RESTfulness und Benutzerfreundlichkeit.
Mike Partridge
Mein Hauptanliegen ist hier etwas praktischer. Ich verwende ASP.NET MVC, um die Web-App zu implementieren, und aufgrund einiger interner Regeln muss ich URL-Muster definieren, die die App unterstützt. Dh wenn / a / {id} definiert ist, wird / a / 1 von der App verarbeitet, nicht jedoch / a / 1 / b / 2. Dies führt zu der Anforderung, die Webanwendung neu zu kompilieren, wenn sich die REST-API-URLs ändern, um nicht nur mit Lesezeichen versehene URLs beizubehalten, sondern auch, um die Webanwendung beim Navigieren vom Stammverzeichnis aus einfach funktionsfähig zu machen. Einfach, weil in HTML-Seiten eingebettete Hyperlinks ohne das nicht funktionieren.
Pavel Gatilov
2

Seien wir ehrlich, es gibt keine magische Lösung. Haben Sie Richardson Maturity Model gelesen ? Es unterteilt die Reife der REST-Architektur in drei Ebenen: Ressourcen, HTTP-Verben und Hypermedia-Steuerelemente.

Ich sollte keine unformatierten Bezeichner meiner Entitäten offen legen, sondern Hyperlinks mit rel = "self" zurückgeben.

Dies sind Hypermedia-Steuerelemente. Brauchen Sie es wirklich? Dieser Ansatz hat einige sehr gute Vorteile (Sie können darüber hier lesen ). Es gibt jedoch keine kostenlosen Mahlzeiten und Sie müssen hart arbeiten (z. B. Ihre zweite Lösung), wenn Sie diese erhalten möchten.

Es ist eine Frage des Gleichgewichts - möchten Sie Leistung opfern (und Ihren Code komplizierter machen), aber ein flexibleres System erhalten? Oder ziehen Sie es vor, die Dinge schneller und einfacher zu halten, aber später zu zahlen, wenn Sie Änderungen an Ihrer API / Ihrem Modell vornehmen?

Als jemand, der ein ähnliches System entwickelt hat (Business Logic Tier, Web Tier und Web Clients), habe ich die zweite Option gewählt. Da meine Gruppe alle Ebenen entwickelt hat, haben wir beschlossen, dass es besser ist, ein wenig zu koppeln (indem wir die Webebene über Entitäts-IDs und das Erstellen von API-URLs informieren) und im Gegenzug Code zu erhalten, der einfacher ist. Die Abwärtskompatibilität war in unserem Fall ebenfalls nicht relevant.

Wenn die Webanwendung von einem Drittanbieter entwickelt wurde oder wenn Abwärtskompatibilität ein Problem war, haben wir möglicherweise eine andere Wahl getroffen, da es von großem Wert war, die URL-Struktur ändern zu können, ohne die Webanwendung zu ändern. Grund genug, den Code zu komplizieren.

Bedeutet das alles, dass RESTful-APIs nicht als Back-End für Webanwendungen dienen können?

Ich denke, das bedeutet, dass Sie keine perfekte REST-Implementierung erstellen müssen. Sie können mit Ihrer zweiten Lösung fortfahren , Entity- IDs offenlegen oder API-URLs übergeben . Es ist in Ordnung, solange Sie die Implikationen und Kompromisse verstehen.

daramasala
quelle
0

Ich denke, wenn du an etwas ähnlichem festhältst, Atom Syndication Formatbist du gut.

Hier können die Metadaten, die den dargestellten Eintrag beschreiben, mit zusätzlichen Elementen / Attributen angegeben werden:

  • Enthält gemäß [RFC4287] eine URI, die den Eintrag eindeutig identifiziert

  • Wie pro [RFC4287] , ist dieses Element optional. Wenn enthalten, enthält es den URI, den ein Client zum Abrufen des Eintrags verwenden soll.

Das sind nur meine zwei Cent.

Rusev
quelle
Vielleicht verstehe ich etwas nicht, aber es scheint mir, dass Ihre Antwort nicht erklärt, wie URLs einer Web-App generiert werden, die ein Client für eine REST-API ist, oder?
Pavel Gatilov
0

Sorgen Sie sich nicht um die URLs, sondern um die Medientypen.

Siehe hier (insbesondere dritter Punkt).

Eine REST-API sollte fast den gesamten beschreibenden Aufwand für die Definition des Medientyps (der Medientypen) verwenden, der zur Darstellung von Ressourcen und zur Steuerung des Anwendungszustands verwendet wird, oder für die Definition erweiterter Beziehungsnamen und / oder hypertextfähiger Markierungen für vorhandene Standardmedientypen. .


Bei einer typischen Web-App ist der Client ein Mensch . Der Browser ist nur ein Agent .

So ein Anchor-Tag gefällt

          <a href="example.com/foo/123">click here</a>

entspricht sowas

          <link type="text/html" rel="self" href="example.com/foo/123">

Die URL ist für die Benutzerin immer noch undurchsichtig. Sie kümmert sich nur um die Medientypen (z text/html, application/pdf, application/flv, video/x-flv, image/jpeg, image/funny-cat-picture etc. B. ). Der im Anker (und im title-Attribut) enthaltene beschreibende Text ist nur eine Möglichkeit, den Beziehungstyp so zu erweitern, dass er für den Menschen verständlich ist.

Der Grund dafür, dass der URI für Clients undurchsichtig sein soll, besteht darin, dass Sie die Kopplung reduzieren (eines der Hauptziele von REST). Der Server kann die URIs ändern / reorganisieren, ohne dass dies Auswirkungen auf den Client hat (sofern Sie eine gute Caching-Richtlinie haben - was möglicherweise überhaupt kein Caching bedeutet).

Zusammenfassend

Stellen Sie nur sicher, dass sich der Client (Mensch oder Maschine) um die Medientypen und die Beziehungen kümmert und nicht um die URLs.

Rodrick Chapman
quelle
Rodrick, meine Frage bezieht sich nicht auf das Erstellen der API, sondern auf das Erstellen einer Webanwendung, die sich auf einer RESTful-API befindet. Ich kann kaum verstehen, wie Medientypen mir beim Erstellen von URLs für die Web-App helfen können. Obwohl Medientypen für den Servicevertrag und die Auffindbarkeit entscheidend sind.
Pavel Gatilov
@PavelGatilov - Ist der Client Ihrer Web-App ein Mensch?
Rodrick Chapman
Ja ist es. Und eine sehr wenig qualifizierte.
Pavel Gatilov
0

Am einfachsten ist es wahrscheinlich, die API-URLs von Ressourcen als Zeichenfolgen-IDs zu verwenden. Webseiten-URLs wie http://my.web.app/person/http%3A%2F%2Fmy.rest.api%2Fapi%2Fperson%2F1234 sind jedoch hässlich.

Ich denke du hast recht, das ist der einfachste Weg. Sie können die URLs relativieren http://my.rest.api/api, um sie weniger hässlich zu machen:

http://my.web.app/person/person%2F1234

Wenn die von der API bereitgestellte URL nicht relativ zu dieser Basis ist, verschlechtert sie sich in die hässliche Form:

http://my.web.app/person/http%3A%2F%2Fother.api.host%2Fapi%2Fperson%2F1234

Um noch einen Schritt weiter zu gehen, überprüfen Sie die Antwort vom API-Server, um festzustellen, welche Art von Ansicht Sie darstellen möchten, und beenden Sie die Codierung des Pfadsegmenttrennzeichens und der Doppelpunkte:

http://my.web.app/person/1234 (best case)
http://my.web.app/http://other.api.host/api/person/1234 (ugly case)
Ajlane
quelle