HATEOAS: absolute oder relative URLs?

84

Welche Vor- und Nachteile hat das Entwerfen eines Links als vollständige URL (" http: // Server: Port / Anwendung / Kunden / 1234 ") im Vergleich zum Pfad ("/ application /" beim Entwerfen eines RESTful-Webdienstes mit HATEOAS? Kunden / 1234 ")?

Mark Lutton
quelle

Antworten:

83

Es gibt eine subtile konzeptionelle Ambiguität, wenn Leute "relative URI" sagen.

Nach der Definition von RFC3986 enthält ein generischer URI:

  URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]

  hier-part   = "//" authority path-abempty
              / path-absolute
              / path-rootless
              / path-empty

     foo://example.com:8042/over/there?name=ferret#nose
     \_/   \______________/\_________/ \_________/ \__/
      |           |            |            |        |
   scheme     authority       path        query   fragment

Das Schwierige ist, wenn Schema und Autorität weggelassen werden, kann der "Pfad" -Teil selbst entweder ein absoluter Pfad (beginnt mit /) oder ein "wurzelloser" relativer Pfad sein. Beispiele:

  1. Ein absoluter URI oder ein vollständiger URI:"http://example.com:8042/over/there?name=ferret"
  2. Und dies ist ein relativer Uri mit absolutem Pfad :/over/there
  3. Und dies ist ein relativer Uri mit einem relativen Pfad : hereoder ./hereoder ../hereoder usw.

Wenn also die Frage lautete "ob ein Server in einer erholsamen Antwort einen relativen Pfad erzeugen soll ", lautet die Antwort "Nein" und der detaillierte Grund ist hier verfügbar . Ich denke, die meisten Leute (einschließlich mir) gegen "relative URI" sind tatsächlich gegen "relativen Pfad".

In der Praxis können die meisten serverseitigen MVC-Frameworks leicht einen relativen URI mit einem absoluten Pfad wie z. B. generieren /absolute/path/to/the/controller, und die Frage lautet: "Die Serverimplementierung sollte scheme://hostname:portdem absoluten Pfad ein Präfix voranstellen ." Wie die Frage des OP. Da bin ich mir nicht ganz sicher.

Einerseits denke ich immer noch, dass ein Server, der eine vollständige URL zurückgibt, empfohlen wird. Der Server sollte das hostname:portDing im Quellcode jedoch niemals so fest codieren (andernfalls würde ich lieber auf relative URL mit absolutem Pfad zurückgreifen). Die Lösung besteht darin, dass das Server-Präfix serverseitig immer aus dem "Host" -Header der HTTP-Anforderung abgerufen wird. Ich bin mir nicht sicher, ob dies in jeder Situation funktioniert.

Andererseits scheint es für den Client nicht sehr mühsam zu sein, den http://example.com:8042und den absoluten Pfad zu verketten . Immerhin kennt der Client dieses Schema und diesen Domainnamen bereits, wenn er die Anfrage an den Server sendet, oder?

Alles in allem würde ich empfehlen, einen absoluten URI zu verwenden, möglicherweise auf einen relativen URI mit absolutem Pfad zurückzugreifen, niemals einen relativen Pfad zu verwenden .

RayLuo
quelle
2
Dies ist eine gute Antwort (+1), der ich mit Ausnahme der endgültigen Schlussfolgerung zustimme. In meiner Antwort argumentiere ich jedoch, dass die HTTP-Spezifikation beispielsweise "absolut" definiert, um auf einen absoluten Pfad zu verweisen , nicht auf einen vollständig qualifizierten URI. Ich bin also nicht mit Ihrer (2) einverstanden - es handelt sich um eine absolute URI, für die der Client jedoch auf das Netzwerkprotokoll und den Host schließen muss, sodass es sich nicht um eine vollständig qualifizierte URI handelt. Und deshalb stimme ich auch Ihrer Definition von (1) nicht zu, die sowohl eine vollständige als auch eine absolute URI ist.
Lawrence Dol
Danke für den Kommentar. Ich leihe mir nur den absoluten Pfad und das relative Pfadkonzept aus dem Dateisystem aus. Abgesehen von anderen Begriffen sehe ich keinen wesentlichen Unterschied zwischen Ihrer und meiner Meinung. Sie empfehlen auch Form 1 & 2 und Sie gegen Form 3, nicht wahr?
RayLuo
2
Praktisch bin ich für (2); Ich denke, (1) erfordert, dass das Backend über zu viel HTTP-spezifisches Wissen verfügt (dh über die Details der spezifischen HTTP-Umgebung, nicht über HTTP im Allgemeinen), und (3) scheint zu viel vom Client zu erfordern. Aber war meine Argumentation auf dem ursprünglichen Entwurf Spezifikation basiert, und die Beispiele wurden in einer späteren Version in einer Weise verändert , die meine Argumentation entkräftet.
Lawrence Dol
Persönlich bin ich (noch) nicht davon überzeugt, dass HATEOAS und daher die Forderung nach Rückgabe von URIs für eine API allzu viel Sinn macht. Ich sehe nur nicht, dass meine APIs auf dem Client auf eine Art und Weise gesteuert werden, die dem Surfen auf einer Website ähnelt. Die Anwendungsfälle scheinen sehr stark von der Ad-hoc-Funktion bestimmt zu sein.
Lawrence Dol
@ LawrenceDol Ich habe die gleiche Verwirrung über HATEOAS am Anfang. Jetzt betrachte ich es als eine Frage der Wahl. Ihre Kunden können die Ad-hoc-Funktion verwenden, um Ihre API sicher zu verwenden. Wenn sie möchten, können sie dennoch ein Muster entwickeln, dem sie folgen können, damit der Kunde nicht jede exakte URL hart codieren muss. Das ist HATEOAS.
RayLuo
13

Es hängt davon ab, wer den Client-Code schreibt. Wenn Sie den Client und den Server schreiben, macht dies keinen großen Unterschied. Sie werden entweder unter dem Schmerz leiden, die URLs auf dem Client oder auf dem Server zu erstellen.

Wenn Sie jedoch den Server erstellen und erwarten, dass andere Benutzer Client-Code schreiben, werden sie Sie viel mehr lieben, wenn Sie vollständige URIs bereitstellen. Das Auflösen relativer URIs kann etwas schwierig sein. Wie Sie sie zuerst auflösen, hängt vom zurückgegebenen Medientyp ab. HTML hat das Basis-Tag, XML kann xml haben: Basis-Tags in jedem verschachtelten Element, Atom-Feeds können eine Basis im Feed und eine andere Basis im Inhalt haben. Wenn Sie Ihrem Client keine expliziten Informationen über den Basis-URI zur Verfügung stellen, muss er den Basis-URI aus dem Anforderungs-URI oder möglicherweise aus dem Content-Location-Header abrufen! Und achten Sie auf diesen nachlaufenden Schrägstrich. Der Basis-URI wird ermittelt, indem alle Zeichen rechts vom letzten Schrägstrich ignoriert werden. Dies bedeutet, dass der nachgestellte Schrägstrich jetzt beim Auflösen relativer URIs von großer Bedeutung ist.

Das einzige andere Problem, das eine kleine Erwähnung erfordert, ist die Dokumentgröße. Wenn Sie eine große Liste von Elementen zurückgeben, bei denen jedes Element mehrere Links haben kann, kann die Verwendung absoluter URLs Ihrer Entität eine erhebliche Anzahl von Bytes hinzufügen, wenn Sie die Entität nicht komprimieren. Dies ist ein Perf-Problem, und Sie müssen von Fall zu Fall entscheiden, ob es von Bedeutung ist.

Darrel Miller
quelle
11

Der einzige wirkliche Unterschied scheint zu sein, dass es für Clients einfacher ist, absolute URIs zu verwenden, anstatt sie aus der relativen Version erstellen zu müssen. Natürlich würde dieser Unterschied ausreichen, um mich dazu zu bewegen, die absolute Version zu machen.

Hank Gay
quelle
7

Wenn Ihre Anwendung skaliert, möchten Sie möglicherweise einen Lastenausgleich, ein Failover usw. durchführen. Wenn Sie absolute URIs zurückgeben, folgen Ihre clientseitigen Apps Ihrer sich entwickelnden Serverkonfiguration.

CyberFonic
quelle
Vorausgesetzt, Sie definieren "absolut" als absoluten Pfad (z. B. /xxx/yyy...) und nicht als vollqualifizierten URI (z http://api.example.com/xxx/yyy.... B. ).
Lawrence Dol
6

Verwendung der RayLou-Trichotomie hat sich meine Organisation für die Bevorzugung entschieden (2). Der Hauptgrund besteht darin, XSS-Angriffe (Cross-Site Scripting) zu vermeiden. Das Problem besteht darin, dass nachfolgende Benutzeranforderungen (z. B. eine Authentifizierungsanforderung mit Benutzername und Kennwort) an den eigenen Server des Angreifers * weitergeleitet werden können, wenn ein Angreifer sein eigenes URL-Stammverzeichnis in die vom Server zurückgegebene Antwort einfügen kann.

Einige haben das Problem angesprochen, Anforderungen für den Lastenausgleich an andere Server umleiten zu können, aber (obwohl dies nicht mein Fachgebiet ist) ich würde wetten, dass es bessere Möglichkeiten gibt, den Lastenausgleich zu aktivieren, ohne Clients explizit auf andere umleiten zu müssen Gastgeber.

* Bitte lassen Sie mich wissen, wenn diese Argumentation Mängel aufweist. Das Ziel ist natürlich nicht, alle Angriffe zu verhindern, sondern mindestens einen Angriffsweg.

Rahs
quelle
Ich bin froh, dass meine vorherige Antwort für Ihre Organisation hilfreich war. Ja, ich persönlich bevorzuge auch (2), auch bekannt als schemaloser absoluter Pfad. Ich bin jedoch neugierig auf Ihre Argumentation. Wie haben Sie Ihren Kunden dazu gebracht, nur Ihre schemalose URL zu akzeptieren? Ein generischer Client wie ein Browser würde eine schemalose URL überhaupt nicht ablehnen. Ich gehe also davon aus, dass Sie Ihren eigenen clientseitigen Code schreiben müssen, um URLs zu validieren, bevor Sie ihnen tatsächlich folgen können. Obwohl dies technisch machbar (aber nicht unbedingt nützlich) ist, ist diese Art der clientseitigen Validierung normalerweise nicht Teil der REST- oder HATEOAS-Diskussion.
RayLuo
3
Ich weiß, dass dies ein alter Beitrag ist, aber ich möchte nur darauf hinweisen, dass "wenn ein Angreifer seine eigene URL-Wurzel in die zurückkommende Antwort einfügen kann" ein Unsinnsgrund ist. Wenn sie "ihre eigene URL" an den richtigen Stellen in der Antwort einfügen können, können sie wahrscheinlich genauso einfach Ihren Hostnamen durch ihren eigenen ersetzen. Aus Sicherheitsgründen sehe ich dies nicht als gültiges Argument.
Magnus Eriksson
5

Sie sollten immer die vollständige URL verwenden. Es fungiert als eindeutige Kennung für die Ressource, da alle URLs eindeutig sein müssen.

Ich würde auch argumentieren, dass Sie konsequent sein sollten. Da der Standort-HTTP-Header eine vollständige URL basierend auf der HTTP-Spezifikation erwartet, wird die vollständige URL im Standort-Header an den Client zurückgesendet, wenn eine neue Ressource erstellt wird. Es wäre seltsam für Sie, eine vollständige URL im Location-Header und dann relative URIs in den Links in Ihrem Antworttext anzugeben.

Mark Bober
quelle
1
Nun, die HTTP-Spezifikation für den Location-Header sagt absolute URI. Ein absoluter URI muss ein Schema enthalten (z. B. http).
Mark Bober
Die Frage ist jedoch nicht, wie kontextlose undurchsichtige Bezeichner erstellt werden sollen , sondern wie Verknüpfungen erstellt werden sollen . Letzterer kann zu Recht "am selben Netzwerkspeicherort wie dieses Dokument" schließen, und genau das gibt das Beispiel eines LocationHeaders in der Spezifikation an - einen absoluten URI, der weder das URI-Schema noch den Netzwerkspeicherort des Servers enthält. Während Links und IDs oft zusammengeführt werden, sind sie nicht dasselbe - ersteres hat Kontext, letzteres nicht.
Lawrence Dol
Können Sie einen Link zu dem Teil der Spezifikation senden, über den Sie sprechen?
Mark Bober
Ein absoluter URI gibt ein Schema an. Ein URI, der nicht absolut ist, wird als relativ bezeichnet. URIs werden auch danach klassifiziert, ob sie undurchsichtig oder hierarchisch sind. Ein undurchsichtiger URI ist ein absoluter URI, dessen schemaspezifischer Teil nicht mit einem Schrägstrich ('/') beginnt. Undurchsichtige URIs unterliegen keiner weiteren Analyse. Einige Beispiele für undurchsichtige URIs sind: mailto: [email protected] news: comp.lang.java urn: isbn: 096139210x
Mark Bober
1
Hey, keine Sorge, Mann. Ein weiterer Punkt bei diesem Zeug ist, dass ich Leute gesehen habe, die hrefs als IDs verwenden. Damit der Client die URL nicht aus einer Konfigurationsdatei und einer ID rekonstruieren muss, kennt er nur die URL und kann darauf basierend zwischenspeichern.
Mark Bober
2

Eine wichtige Überlegung bei großen API-Ergebnissen ist der zusätzliche Netzwerkaufwand für das wiederholte Einbeziehen des vollständigen URI. Ob Sie es glauben oder nicht, gzip löst dieses Problem nicht vollständig (nicht sicher warum). Wir waren schockiert darüber, wie viel Speicherplatz die vollständige URI einnahm, als ein Ergebnis Hunderte von Links enthielt.

George Sibble
quelle
2

Ein Nachteil der Verwendung absoluter URIs besteht darin, dass die API nicht als Proxy verwendet werden kann.

Nimm es zurück ... nicht wahr. Sie sollten eine vollständige URL einschließlich der Domain angeben.

Jay Pete
quelle
3
Warum kann der absolute URI den Hostnamen des Proxys nicht verwenden?
Ed Summers
1
Arbeiten Sie genau dieses Problem im Moment durch. Wir möchten, dass alle Anforderungen zuerst eine Art "Lastausgleichs" -Schicht durchlaufen. Absolute URIs für die Server direkt brechen dieses Modell.
Mag382
1
Ich verwende Nginx, um eine Site mit absoluten URLs zu vertreten. Es ist perfekt in der Lage, die Backend-URL durch die entsprechende Proxy-URL zu ersetzen. Insbesondere wird windyroad.artifactoryonline.com (mit vollständig qualifizierten URLs und vollständig qualifizierten Weiterleitungen) an repo.windyroad.com.au weitergeleitet
Tom Howard
2

In Bezug auf die Profis sehe ich die Reduzierung der zu übertragenden Bytes auf Kosten einer zusätzlichen Behandlung, die ein Client für den (absoluten) Pfad benötigt. Wenn Sie unbedingt jedes Byte speichern möchten, selbst nachdem Sie die Inhaltscodierung als gzip, die ordnungsgemäße Verwendung von Caching-Headern, die Verwendung von Etags und bedingte Anforderungen auf dem Client versucht haben, kann dies am Ende erforderlich sein, aber ich erwarte viel höhere Renditen Ihre Bemühungen waren anderswo.

In Bezug auf die Nachteile sehe ich einen Kontrollverlust darüber, wie Sie den Clientfluss zwischen Ressourcen in Zukunft steuern können (Lastausgleich, A / B-Tests, ...), und ich würde dies als schlechte Praxis bei der Verwaltung eines Webs betrachten API. Die von Ihnen angegebene URL ist für den Client nicht mehr grundsätzlich undurchsichtig (siehe Tim Berners-Lee Axiome der Webarchitektur zur URI-Opazität ). Am Ende sind Sie dafür verantwortlich, die Kunden über die kreative Nutzung Ihrer API zufrieden zu stellen, auch wenn es nur um die Struktur Ihres URL-Bereichs geht. Sollten Sie jemals eine explizit definierte URL-Änderung zulassen müssen, sollten Sie die Verwendung von URI-Vorlagen in Betracht ziehen, wie sie in der Hypertext-Anwendungssprache verwendet werden .

Michael Hartle
quelle