HATEOAS: URL assoluti o relativi?


Risposte:


83

C'è una sottile ambiguità concettuale quando le persone dicono "URI relativo".

Secondo la definizione di RFC3986 , un URI generico contiene:

  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

La cosa complicata è che, quando schema e autorità vengono omessi, la parte "percorso" stessa può essere un percorso assoluto (inizia con /) o un percorso relativo "senza radice". Esempi:

  1. Un URI assoluto o un URI completo:"http://example.com:8042/over/there?name=ferret"
  2. E questo è un uri relativo, con percorso assoluto :/over/there
  3. E questo è un uri relativo, con percorso relativo : hereor ./hereor ../hereor ecc.

Quindi, se la domanda era "se un server deve produrre un percorso relativo in una risposta riposante", la risposta è "No" e il motivo dettagliato è disponibile qui . Penso che la maggior parte delle persone (incluso me) contro "URI relativo" sia in realtà contro "percorso relativo".

E in pratica, la maggior parte dei framework MVC lato server può facilmente generare URI relativo con un percorso assoluto come /absolute/path/to/the/controller, e la domanda diventa "se l'implementazione del server deve anteporre a un scheme://hostname:portdavanti al percorso assoluto". Come la domanda dell'OP. Non sono abbastanza sicuro di questo.

Da un lato, penso ancora che il server che restituisce un uri completo sia consigliato. Tuttavia, il server nonhostname:port dovrebbe mai codificare la cosa all'interno del codice sorgente come questo (altrimenti preferirei eseguire il fallback su uri relativo con percorso assoluto). La soluzione è lato server che ottiene sempre quel prefisso dall'intestazione "Host" della richiesta HTTP. Non sono sicuro che funzioni per tutte le situazioni.

D'altra parte, non sembra molto problematico per il client concatenare il http://example.com:8042percorso assoluto e. Dopo tutto, il client conosce già quello schema e il nome di dominio quando invia la richiesta al server, giusto?

Tutto sommato, direi, consiglio di usare l'URI assoluto, possibilmente fallback all'URI relativo con percorso assoluto, non usare mai il percorso relativo .


2
Questa è una buona risposta (+1) con la quale sono d'accordo tranne la conclusione finale. Tuttavia, nella mia risposta, sostengo che la specifica HTTP definisce, ad esempio , "assoluto" per fare riferimento a un percorso assoluto , non a un URI pienamente qualificato. Quindi non sono d'accordo con il tuo (2) - è un URI assoluto, ma per il quale il client deve dedurre il protocollo di rete e l'host, quindi non è un URI pienamente qualificato. E, quindi, sono anche in disaccordo con la tua definizione di (1) che è sia un URI completo che un URI assoluto.
Lawrence Dol

Grazie per il commento. Ho appena preso in prestito il percorso assoluto e il concetto di percorso relativo dal file system. Termini diversi a parte, non vedo differenze sostanziali tra la tua opinione e la mia. Raccomandi anche il modulo 1 e 2 e tu contro il modulo 3, vero?
RayLuo

2
In pratica, io sono per (2); Penso che (1) richieda che il backend abbia molta conoscenza specifica di HTTP (ovvero i dettagli dello specifico ambiente HTTP, non HTTP in generale), e (3) sembra richiedere troppa conoscenza del client. Ma il mio ragionamento era basato sulla bozza delle specifiche originali e gli esempi sono stati modificati in una versione successiva in un modo che invalida il mio ragionamento.
Lawrence Dol

Personalmente, non sono (ancora) per niente convinto che HATEOAS, e quindi la richiesta di restituire gli URI ha molto senso per un'API. Semplicemente non vedo le mie API guidate sul client in un modo simile alla navigazione in un sito web; i casi d'uso sembrano molto guidati dalla funzione ad hoc.
Lawrence Dol

@ LawrenceDol Ho la stessa confusione su HATEOAS all'inizio. Ora lo considero una questione di scelta. I tuoi clienti possono utilizzare la funzione ad hoc per consumare sicuramente la tua API, ma se lo desiderano, possono comunque sviluppare un modello da seguire, in modo che il client non debba codificare in modo rigido ogni URL esatto. Questo è HATEOAS.
RayLuo

13

Dipende da chi scrive il codice client. Se stai scrivendo client e server, non fa molta differenza. O soffrirai il dolore di costruire gli URL sul client o sul server.

Tuttavia, se stai costruendo il server e ti aspetti che altre persone scrivano il codice client, ti ameranno molto di più se fornisci URI completi. Risolvere gli URI relativi può essere un po 'complicato. Innanzitutto come risolverli dipende dal tipo di supporto restituito. Html ha il tag di base, Xml può avere xml: tag di base in ogni elemento nidificato, i feed Atom potrebbero avere una base nel feed e una base diversa nel contenuto. Se non fornisci al tuo cliente informazioni esplicite sull'URI di base, deve ottenere l'URI di base dall'URI della richiesta, o forse dall'intestazione Content-Location! E fai attenzione a quel taglio finale. L'URI di base viene determinato ignorando tutti i caratteri a destra dell'ultima barra. Ciò significa che la barra finale è ora molto significativa quando si risolvono gli URI relativi.

L'unico altro problema che richiede una piccola menzione è la dimensione del documento. Se restituisci un ampio elenco di elementi in cui ogni elemento può avere più collegamenti, l'utilizzo di URL assoluti può aggiungere una quantità significativa di byte alla tua entità se non comprimi l'entità. Questo è un problema di prestazioni e devi decidere se è significativo caso per caso.


11

L'unica vera differenza sembrerebbe essere che è più facile per i client se consumano URI assoluti invece di doverli costruire dalla versione relativa. Ovviamente quella differenza sarebbe sufficiente per convincermi a fare la versione assoluta.


7

Man mano che la tua applicazione si ridimensiona, potresti voler eseguire il bilanciamento del carico, il failover, ecc. Se restituisci URI assoluti, le tue app lato client seguiranno la tua configurazione in evoluzione dei server.


A condizione di definire "assoluto" come percorso assoluto (ad esempio /xxx/yyy...) e non come un URI pienamente qualificato (ad esempio http://api.example.com/xxx/yyy...).
Lawrence Dol

6

Utilizzando la tricotomia di RayLou, la mia organizzazione ha optato per la preferenza (2). Il motivo principale è evitare gli attacchi XSS (Cross-Site Scripting). Il problema è che se un utente malintenzionato può inserire la propria radice URL nella risposta che torna dal server, le successive richieste dell'utente (come una richiesta di autenticazione con nome utente e password) possono essere inoltrate al server dell'attaccante *.

Alcuni hanno sollevato il problema di essere in grado di reindirizzare le richieste ad altri server per il bilanciamento del carico, ma (sebbene questa non sia la mia area di competenza) scommetterei che ci sono modi migliori per abilitare il bilanciamento del carico senza dover reindirizzare esplicitamente i client a diversi host.

* per favore fatemi sapere se ci sono dei difetti in questo ragionamento. L'obiettivo, ovviamente, non è impedire tutti gli attacchi, ma almeno una via di attacco.


Sono contento che la mia precedente risposta sia stata utile alla tua organizzazione. Sì, personalmente preferisco anche (2), aka percorso assoluto senza schema. Tuttavia sono curioso del tuo ragionamento. Come hai fatto a imporre al tuo cliente di accettare solo il tuo URL senza schema? Un client generico, come un browser, non rifiuterebbe affatto un URL senza schema. Quindi presumo che dovresti scrivere il tuo codice lato client per convalidare gli URL prima di seguirli effettivamente? Sebbene ciò sia tecnicamente fattibile (ma non necessariamente utile), questo tipo di convalida lato client in genere non fa parte delle discussioni REST o HATEOAS.
RayLuo

3
So che questo è un vecchio post, ma voglio solo sottolineare che "se un utente malintenzionato può iniettare la propria radice URL nella risposta che torna" è una ragione senza senso. Se possono "inserire il proprio URL" nelle posizioni corrette della risposta, scommetto che potrebbero, altrettanto facilmente, sostituire il tuo hostname con il proprio. Quindi, dal punto di vista della sicurezza, non lo vedo come un argomento valido.
Magnus Eriksson

5

Dovresti sempre utilizzare l'URL completo. Agisce come identificatore univoco per la risorsa poiché tutti gli URL devono essere univoci.

Direi anche che dovresti essere coerente. Poiché l'intestazione HTTP Location prevede un URL completo in base alla specifica HTTP, l'URL completo viene restituito nell'intestazione Location al client quando viene creata una nuova risorsa. Sarebbe strano per te fornire un URL completo nell'intestazione Location e quindi gli URI relativi nei collegamenti all'interno del corpo della risposta.


1
Ebbene, la specifica HTTP per l'intestazione Location dice URI assoluto. Un URI assoluto deve contenere uno schema (ad esempio http).
Mark Bober

Ma la domanda non è come costruire identificatori opachi senza contesto , ma come costruire collegamenti . Quest'ultimo può giustamente dedurre "nella stessa posizione di rete di questo documento", ed è esattamente ciò che Locationfornisce l'esempio di intestazione delle specifiche: un URI assoluto che non contiene lo schema URI o il percorso di rete del server. Sebbene i collegamenti e gli ID siano spesso combinati, non sono la stessa cosa: il primo ha un contesto, il secondo no.
Lawrence Dol

Puoi inviare un collegamento alla parte della specifica di cui stai parlando?
Mark Bober

Un URI assoluto specifica uno schema; un URI che non è assoluto si dice che sia relativo. Gli URI vengono classificati anche a seconda che siano opachi o gerarchici. Un URI opaco è un URI assoluto la cui parte specifica dello schema non inizia con un carattere barra ("/"). Gli URI opachi non sono soggetti a ulteriore analisi. Alcuni esempi di URI opachi sono: mailto: java-net@java.sun.com news: comp.lang.java urn: isbn: 096139210x
Mark Bober

1
Hey nessun problema amico. Un altro punto su questa roba è che ho visto persone che usano hrefs come ID. In modo che il client non debba ricostruire l'URL da un file di configurazione e un id, conosce semplicemente l'URL e può memorizzare nella cache in base ad esso.
Mark Bober

2

Una considerazione importante nei risultati di API di grandi dimensioni è il sovraccarico di rete aggiuntivo derivante dall'inclusione ripetuta dell'URI completo. Che tu ci creda o no, gzip non risolve completamente questo problema (non so perché). Siamo rimasti scioccati dallo spazio occupato dall'URI completo quando c'erano centinaia di collegamenti inclusi in un risultato.


2

Uno svantaggio dell'utilizzo di URI assoluti è che l'API non può essere sottoposto a proxy.

Riprendilo ... non è vero. Dovresti cercare un URL completo che includa il dominio.


3
Perché l'URI assoluto non può utilizzare il nome host del proxy?
Ed Summers

1
Al momento stiamo risolvendo questo problema esatto. Vogliamo che tutte le richieste passino prima attraverso una sorta di livello di "bilanciamento del carico". Gli URI assoluti diretti ai server interromperanno questo modello.
mag382

1
Sto usando Nginx per eseguire il proxy di un sito con URL assoluti. È perfettamente in grado di sostituire l'URL di backend con l'URL proxy equivalente. In particolare, sta inviando il proxy windyroad.artifactoryonline.com (che ha URL completamente qualificati e reindirizzamenti pienamente qualificati) a repo.windyroad.com.au
Tom Howard

2

Per quanto riguarda i pro, vedo la riduzione dei byte da trasmettere a scapito della gestione extra richiesta da un client per il percorso (assoluto). Se desideri disperatamente salvare ogni byte, anche dopo aver provato la codifica del contenuto come gzip, l'uso corretto delle intestazioni di memorizzazione nella cache, l'uso di etags e richieste condizionali sul client, allora questo potrebbe essere necessario alla fine, ma mi aspetto ritorni molto più alti su i tuoi sforzi altrove.

Per quanto riguarda i contro, vedo una perdita di controllo su come puoi dirigere il flusso dei clienti tra le risorse in futuro (bilanciamento del carico, test A / B, ...), e la considererei una cattiva pratica per quanto riguarda la gestione di un web API. L'URL fornito non è più fondamentalmente opaco per il client (vedere Tim Berners-Lee Axioms of Web Architecture on URI opacity ). Alla fine, diventi responsabile di mantenere i clienti soddisfatti del loro utilizzo creativo della tua API, anche se riguarda solo la struttura del tuo spazio URL. Se è necessario consentire una modifica dell'URL definita in modo esplicito, prendere in considerazione l'utilizzo di modelli URI utilizzati nel linguaggio dell'applicazione ipertestuale .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.