Un'applicazione Web come client API REST: come gestire gli identificatori di risorse


21

Diversi concetti legati al conflitto REST nella mia testa quando provo a implementarlo.

Ho un sistema API back-end REST completo che detiene la logica aziendale e un'applicazione Web che fornisce l'interfaccia utente. Da varie risorse su REST (in particolare, REST in Practice: Hypermedia and Systems Architecture ) so che non dovrei esporre identificatori grezzi delle mie entità, ma piuttosto restituire hyperlink con rel="self".

Considera l'esempio. L'API REST ha una risorsa che restituisce una persona:

<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>

Il problema sorge con l'applicazione Web. Supponiamo che ritorni una pagina che contiene un collegamento ipertestuale ai browser:

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

Cosa devo inserire hrefnell'attributo? Come posso mantenere l'URL dell'entità API nell'applicazione Web per poter ottenere l'entità quando un utente apre la pagina di destinazione?

I requisiti sembrano contrastanti:

  1. Il collegamento ipertestuale hrefdovrebbe portare all'applicazione Web perché è il sistema che ospita l'interfaccia utente
  2. L' hrefdovrebbe avere qualche id dell'entità in quanto l'applicazione web deve essere in grado di affrontare l'entità in cui la pagina di destinazione si apre
  3. L'app Web non dovrebbe analizzare / costruire URL REST perché non è REST, dice il libro citato

Gli URI dovrebbero essere opachi per i consumatori. Solo l'emittente dell'URI sa come interpretarlo e mapparlo su una risorsa.

Quindi, non posso semplicemente prendere 1234dall'URL di risposta dell'API perché come client RESTful dovrei trattarlo come se fosse qualcosa del genere http://my.rest.api/api/AGRIDd~ryPQZ^$RjEL0j. D'altra parte, devo fornire un URL che porta alla mia app Web ed è sufficiente affinché l'app ripristini in qualche modo l'URL originale dell'API e utilizzi tale URL per accedere alle risorse API.

Il modo più semplice consiste probabilmente nell'utilizzare gli URL API delle risorse come identificatori di stringhe. Ma gli URL delle pagine web http://my.web.app/person/http%3A%2F%2Fmy.rest.api%2Fapi%2Fperson%2F1234sono brutti.

Sembra tutto abbastanza semplice per un'app desktop o un'app javascript a pagina singola. Dal momento che vivono continuamente, possono semplicemente mantenere gli URL in memoria insieme agli oggetti di servizio per la durata dell'applicazione e usarli quando necessario.

Con un'app Web posso immaginare diversi approcci, ma sembrano tutti strani:

  1. Sostituisci l'host negli URL API e mantieni solo il risultato. L'enorme svantaggio è che richiede all'applicazione web di gestire qualsiasi URL generato dall'API, il che significa accoppiamento mostruoso. Inoltre, non è di nuovo RESTful, perché la mia app Web inizia a interpretare gli URL.
  2. Esporre gli ID non elaborati nell'API REST insieme ai collegamenti, utilizzarli per creare gli URL delle app Web, quindi utilizzare gli ID sul server delle app Web per trovare le risorse richieste nell'API. Questo è meglio, ma influirà sulle prestazioni del server delle app Web perché l'app Web dovrà passare attraverso la navigazione del servizio REST emettendo una catena di richieste get-by-id di qualche modulo per gestire qualsiasi richiesta da un browser. Per una risorsa un po 'nidificata questo potrebbe essere costoso.
  3. Archivia tutti gli selfURL restituiti dall'API in una mappatura persistente (DB?) Sul server delle app Web. Genera alcuni ID per loro, usa gli ID per creare gli URL della pagina dell'app Web e ottenere gli URL delle risorse del servizio REST. Cioè tengo ilhttp://my.rest.api/pet/678 URL da qualche parte con una nuova chiave, diciamo 3, e generare l'URL della pagina web come http://my.web.app/pet/3. Sembra un'implementazione della cache HTTP di qualche tipo. Non so perché, ma mi sembra strano.

O Significa tutto che le API RESTful non possono fungere da backend per le applicazioni Web?


1
Non è chiaro cosa stai cercando di realizzare, probabilmente perché il tuo intento semplice è coperto dagli strati di architettura che stai mettendo l'uno sull'altro, quindi è difficile dire se le "API RESTful" potrebbero davvero aiutarti. Da quello che ho capito del tuo problema, l'opzione 2 è una soluzione semplice e realizzabile. Il "problema" qui è inerente alle "API RESTful". RestIsJustSqlReinvented e si verificherà effettivamente lo stesso problema quando si tenta di recuperare un sottografo sufficientemente complesso da qualsiasi RDBMS. Usa una cache o una rappresentazione ottimizzata per le tue query.
back2dos

Risposte:


5

Modificato per rispondere agli aggiornamenti delle domande, risposta precedente rimossa

Osservando le modifiche apportate alla tua domanda, penso di capire il problema che stai affrontando un po 'di più. Poiché non esiste un campo che identifichi le tue risorse (solo un collegamento), non hai modo di fare riferimento a quella specifica risorsa all'interno della tua GUI (ovvero un collegamento a una pagina che descrive un animale specifico).

La prima cosa da stabilire è se un animale domestico ha senso senza un proprietario. Se possiamo avere un animale domestico senza alcun proprietario, direi che abbiamo bisogno di una sorta di proprietà unica sull'animale che possiamo usare per fare riferimento ad esso. Non credo che ciò violerebbe la mancata esposizione dell'ID direttamente poiché l'ID risorsa reale sarebbe comunque nascosto in un collegamento che il client REST non analizzerebbe. Tenendo presente ciò, la nostra risorsa per animali domestici potrebbe apparire come:

<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>

Ora possiamo aggiornare il nome di quell'animale da Spot a Fido senza dover fare confusione con gli ID delle risorse in tutta l'applicazione. Allo stesso modo possiamo fare riferimento a quell'animale nella nostra GUI con qualcosa del tipo:

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

Se l'animale non ha alcun senso senza un proprietario (o gli animali non sono ammessi nel sistema senza un proprietario), allora possiamo usare il proprietario come parte dell '"identità" dell'animale nel sistema:

http://example.com/GUI/owners/John/pets/1 (primo animale domestico nell'elenco per John)

Una piccola nota, se animali domestici e persone possono esistere separati l'uno dall'altro, non farei del punto di ingresso per l'API la risorsa "Persone". Invece, creerei una risorsa più generica che contenga un collegamento a Persone e animali domestici. Potrebbe restituire una risorsa che assomiglia a:

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

Quindi, conoscendo solo il primo punto di ingresso nell'API e non elaborando nessuno degli URL per capire gli identificatori di sistema, possiamo fare qualcosa del genere:

L'utente accede all'applicazione. Il client REST accede all'intero elenco di risorse disponibili che può apparire come:

<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>

La GUI eseguiva il ciclo attraverso ogni risorsa e stampava una voce di elenco per ogni persona usando UniqueName come "id":

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

Mentre lo fa, potrebbe anche elaborare ogni collegamento che trova con un rel di "pet" e ottenere la risorsa pet come:

<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>

Usandolo può stampare un link come:

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

o

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

Se andiamo con il primo collegamento e supponiamo che la nostra risorsa di ingresso abbia un collegamento con una relazione di "animali domestici", il flusso di controllo andrebbe qualcosa di simile nella GUI:

  1. La pagina viene aperta e viene richiesto lo Spot per animali domestici.
  2. Carica l'elenco di risorse dal punto di ingresso API.
  3. Carica la risorsa correlata al termine "animali domestici".
  4. Guarda ogni risorsa dalla risposta "animali domestici" e trova quella che corrisponde a Spot.
  5. Visualizza le informazioni per spot.

L'utilizzo del secondo collegamento sarebbe una catena di eventi simile con l'eccezione che People è il punto di accesso all'API e otterremmo prima un elenco di tutte le persone nel sistema, troveremo quello che corrisponde, quindi troveremo tutti gli animali domestici che appartengono a quella persona (usando di nuovo il tag rel) e trova quello che si chiama Spot in modo da poter visualizzare le informazioni specifiche ad esso correlate.


Grazie Mike. Ho aggiornato la mia domanda per renderlo un po 'più chiaro. Il problema con la tua risposta è che non posso essere d'accordo sul fatto che un client REST possa analizzare gli URL. In tal caso, viene accoppiato agli URL. E questo viola una delle idee fondamentali di REST: i client dovrebbero usare rels per scegliere i collegamenti, ma non dovrebbero assumere alcuna conoscenza della struttura degli URL. REST afferma che un'API è libera di modificare gli URL a piacimento a condizione che relrimangano gli stessi. L'analisi degli URL ci avvicina al SOAP piuttosto che al REST.
Pavel Gatilov,

Grazie ancora. Hai descritto l'approccio che abbiamo adottato finora. In un certo senso, esponiamo identificatori. L'unica cosa è che cerchiamo di esporre identificatori naturali quando possibile.
Pavel Gatilov,

6

Significa tutto che le API RESTful non possono fungere da backend per le applicazioni Web?

Mi chiedo se valga la pena distinguere tra un'API REST e un'applicazione Web. La tua "applicazione web" dovrebbe essere solo rappresentazioni alternative (HTML) delle stesse risorse - vale a dire, non capisco come o perché ti aspetti di accedere http://my.rest.api/...ehttp://my.web.app/... e che siano contemporaneamente le stesse e diverse.

Il tuo "client" è il browser in questo caso e comprende HTML e JavaScript. Questo è l'applicazione web secondo me. Ora potresti non essere d'accordo e pensare di accedere a detta applicazione Web usando foo.com ed esporre tutto il resto tramite api.foo.com - ma devi quindi chiedere, in che modo foo.com mi ha fornito la rappresentazione della risorsa? Il "back-end" di foo.com è perfettamente in grado di capire come scoprire risorse da api.foo.com. La tua applicazione web è diventata semplicemente un proxy - non diverso da se stessi parlando con un'altra API (da qualcun altro) tutti insieme.

Quindi la tua domanda può essere generalizzata a "Come posso descrivere le risorse usando i miei URI che esistono in altri sistemi?" il che è banale se si considera che non è il client (HTML / JavaScript) che deve capire come fare, ma il server. Se sei d'accordo con la mia prima sfida, puoi semplicemente pensare alla tua applicazione web come un'API REST separata che inoltra o delega a un'altra API REST.

Quindi quando il tuo client accede my.web.app/pets/1sa di presentare l'interfaccia del pet perché o è quello che è stato restituito dal modello lato server, o se è una richiesta asincrona per qualche altra rappresentazione (ad esempio JSON o XML), l'intestazione del tipo di contenuto lo dice così .

Il server che fornisce questo è il responsabile per capire cos'è un animale domestico e come scoprire un animale domestico sul sistema remoto. Il modo in cui lo fai dipende da te: puoi semplicemente prendere l'ID e generare un altro URI, che ritieni inappropriato, oppure puoi avere il tuo database che memorizza l'URI remoto e inoltra la richiesta. La memorizzazione di questo URI va bene, equivale al segnalibro. Faresti tutto questo solo per avere un nome di dominio separato. Onestamente non so perché lo desideri: anche gli URI dell'API REST devono essere in grado di aggiungere segnalibri.

Hai già sollevato gran parte di questo nella tua domanda, ma sento di averlo inquadrato in un modo che in realtà non ha riconosciuto che è il modo pratico di fare quello che vuoi fare (basato su ciò che ritengo sia un vincolo arbitrario - che l'API e l'applicazione siano separate). Chiedendo se le API REST non possono essere back-end per le applicazioni Web e suggerendo che le prestazioni sarebbero un problema, penso che ti stai concentrando su cose sbagliate. È come dire che non puoi creare un Mashup. È come dire che il web non funziona.


Non mi aspetto che l'app Web sia semplicemente una rappresentazione per l'API. Può avere molte differenze, ad esempio mostrare più risorse secondarie insieme a una radice in una singola pagina. Non voglio che gli URL dell'app Web contengano gli ID interni della memoria dati API, se intendi questo dicendo che mi aspetto che i 2 sistemi siano gli stessi. Non mi preoccupo delle prestazioni qui, non è il problema. La domanda è in realtà "Come inserisco 3 my.web.app/pets/3senza analizzare gli URL API REST"?
Pavel Gatilov

Correzione della mia nuova frase: 'Come inserisco 3 my.web.app/pets/3senza analizzare l'URL della risorsa API REST corrispondente my.rest.api/v0/persons/2/pets/3? O cosa ci metto lì?
Pavel Gatilov

Penso che tu stia confondendo lo stato del client con le rappresentazioni che determinano quello stato. Non mettete 3in app/pets/3causa app/pets/3è opaco, punta a qualsiasi risorsa tua applicazione web vuole. Se si tratta di una vista composta di diverse altre risorse (in altri sistemi - l'API è una di queste), spetta a te archiviare i collegamenti ipertestuali a tali sistemi all'interno del server dell'app Web, quindi recuperarli, risolverli nelle loro rappresentazioni ( ad es. JSON o XML) e poi servirli come parte della tua risposta.
Doug

Pensaci in questo modo - dimentica l'API e l'app. Supponi di voler creare un sito che consenta alle persone di raccogliere i loro post preferiti su Facebook e Twitter. Questi sono sistemi remoti. Non proverai a tunnelizzare o modellare gli URI su tali sistemi tramite i tuoi. Dovresti creare una risorsa 'board' e sarebbe il tuo server a sapere che board/1punta facebook.com/post/123e twitter.com/status/789- quando vai a fornire una rappresentazione della tua board, dovresti risolvere quegli URI in una rappresentazione con cui puoi lavorare. Cache dove necessario.
Doug

E così, poiché vuoi che la tua API sia significativamente diversa dalla tua app (penso ancora che sia discutibile) - trattarla come un sistema remoto non è diversa da quella. Hai detto che le prestazioni non sono il problema, ma hai anche detto nella tua domanda che qualcosa del genere "influenzerebbe le prestazioni".
Doug

5

Prefazione

Questa risposta affronta in modo specifico la domanda su come gestire il proprio schema URL, inclusi URL univoci con segnalibro per risorse per le quali l'API REST back-end non espone esplicitamente un identificatore e senza interpretare gli URL forniti dall'API.


La rilevabilità richiede una certa conoscenza, quindi ecco la mia opinione su uno scenario del mondo reale:

Supponiamo di voler una pagina di ricerca in http://my.web.app/personcui i risultati includano un collegamento alla pagina dei dettagli per ogni persona. L'unica cosa che il nostro codice front-end deve sapere per fare nulla è l'URL di base per la sua origine dati REST: http://my.rest.api/api. La risposta a una richiesta GET a questo URL potrebbe essere:

<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>

Poiché il nostro intento è quello di visualizzare un elenco di persone, successivamente inviamo una GETrichiesta a href dal personlink href, che potrebbe restituire:

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

Vogliamo visualizzare i risultati della ricerca, quindi utilizzeremo il servizio di ricerca inviando una GETrichiesta al searchlink href, che potrebbe restituire:

<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>

Finalmente abbiamo i nostri risultati, ma come possiamo costruire i nostri URL front-end?

Eliminiamo la parte che conosciamo per certo: l'URL di base dell'API e usiamo il resto come identificatore front-end:

  • base API nota: http://my.rest.api/api
  • URL dato per singola entità: http://my.rest.api/api/person/1
  • ID univoco: /person/1
  • il nostro URL di base: http://my.web.app
  • il nostro URL front-end generato: http://my.web.app/person/1

I nostri risultati potrebbero apparire come:

<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>

Una volta che un utente segue quel link front-end alla pagina dei dettagli, a quale URL inviamo la GETrichiesta per i dettagli di quello specifico person? Conosciamo il nostro metodo per mappare gli URL back-end in URL front-end, quindi lo invertiamo semplicemente:

  • URL front-end: http://my.web.app/person/1
  • il nostro URL di base: http://my.web.app
  • ID univoco: /person/1
  • base API nota: http://my.rest.api/api
  • URL API generato: http://my.rest.api/api/person/1

Se l'API REST cambia in modo tale che un personURL sia ora http://my.rest.api/api/different-person-base/person/1e qualcuno avesse precedentemente aggiunto un segnalibro http://my.web.app/person/1, l'API REST dovrebbe (almeno per un certo periodo) fornire compatibilità con le versioni precedenti rispondendo al vecchio URL con un reindirizzamento al nuovo. Tutti i collegamenti front-end generati includeranno automaticamente la nuova struttura.

Come probabilmente avrai notato, ci sono diverse cose che dobbiamo sapere per navigare nell'API:

  • l'URL di base dell'API
  • la personrelazione
  • la searchrelazione

Non penso che ci sia qualcosa di sbagliato in questo; non stiamo assumendo una struttura URL specifica in nessun momento, quindi la struttura dell'URL dell'entità http://my.rest.api/api/person/1potrebbe cambiare e fintanto che l'API fornirà compatibilità con le versioni precedenti, il nostro codice continuerà a funzionare.


Hai chiesto come la nostra logica di routing potesse distinguere tra due URL front-end:

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

Innanzitutto, ti faccio notare che hai utilizzato l'API base nel tuo commento quando nel nostro esempio utilizziamo URL di base separati per l'interfaccia utente e l'API REST. Continuerò l'esempio usando basi separate, ma condividere una base non è un problema. Possiamo (o dovremmo essere in grado di) mappare i metodi di routing dell'interfaccia utente utilizzando il tipo di supporto dall'intestazione Accetta della richiesta.

Per quanto riguarda il routing a una pagina di dettagli specifica, non possiamo differenziare questi due URL se siamo severi nell'evitare qualsiasi conoscenza della struttura selfdell'URL fornita dall'API (cioè l'id stringa opaco). Per farlo funzionare, includiamo un'altra delle nostre informazioni conosciute - il tipo di entità con cui stiamo lavorando - nei nostri URL front-end.

In precedenza i nostri URL front-end erano nel formato: ${UI base}/${opaque string id}

Il nuovo formato potrebbe essere: ${UI base}/${entity type}/${opaque string id}

Quindi, usando l' /person/1esempio, finiremmo con http://my.web.app/person/person/1.

Con questo formato, la nostra logica di routing dell'interfaccia utente funzionerebbe /person/person/1e, sapendo che il primo token nella stringa è stato inserito da noi stessi, possiamo estrarlo e instradare alla pagina di dettaglio appropriata (persona, in questo esempio) basata su di esso. Se ti senti imbarazzato per quell'URL, quindi potremmo inserirne un po 'di più lì dentro; può essere: http://my.web.app/person/detail/person/1

Nel qual caso analizzeremmo il /person/detailrouting e useremo il resto come ID stringa opaco.


Penso che questo introduca un accoppiamento estremamente stretto tra l'app Web e l'API.

Suppongo che intendi dire che, poiché il nostro URL front-end generato contiene parte dell'URL dell'API, se la struttura dell'URL dell'API cambia senza supportare la vecchia struttura, avremo bisogno di una modifica del codice per tradurre l'URL con segnalibro nel nuova versione dell'URL dell'API. In altre parole, se l'API REST modifica l'ID di una risorsa (la stringa opaca), non possiamo parlare al server di quella risorsa utilizzando il vecchio ID. Non credo che possiamo evitare un cambio di codice in quella situazione.

E se volessi che la struttura dell'URL per l'app Web differisse da quella dell'API?

È possibile utilizzare qualsiasi struttura URL desiderata. Alla fine della giornata, un URL con segnalibro per una risorsa specifica deve includere qualcosa che è possibile utilizzare per ottenere un URL API che identifichi in modo univoco quella risorsa. Se si genera il proprio identificativo e lo si memorizza nella cache con l'URL dell'API come nel proprio approccio n. 3, funzionerà fino a quando qualcuno non tenta di utilizzare tale URL con segnalibro dopo che tale voce viene cancellata dalla cache.

Cosa succede se le entità della mia app Web non eseguono il mapping alle entità API 1-1?

La risposta dipende dalla relazione. Ad ogni modo, avresti bisogno di un modo per mappare il front-end agli URL API.


Ho un problema con questo approccio. In realtà è il numero 1 nella mia lista di soluzioni. Quello che non capisco è questo: se l'applicazione Web non interpreta gli URL e tratta ID univoci come stringhe opache (solo person/1, pet/3), allora come sarebbe sapere che se un browser apre http://my.rest.api/api/person/1dovrebbe mostrare persona interfaccia utente, e se si apre http://my.rest.api/api/pet/3, quindi l'interfaccia utente dell'animale domestico?
Pavel Gatilov,

Buona domanda! Ho aggiornato la risposta con la mia risposta.
Mike Partridge,

Grazie Mike. Penso che questo introduca un accoppiamento estremamente stretto tra l'app Web e l'API. E se volessi che la struttura dell'URL per l'app Web differisse da quella dell'API? Cosa succede se le entità della mia app Web non eseguono il mapping alle entità API 1-1? Continuo a pensare che sarebbe meglio adottare l'approccio di esporre alcuni identificatori, ma sollecitare i clienti a utilizzare i collegamenti per la navigazione.
Pavel Gatilov,

Questo è un argomento interessante, quindi spero che non mi manchi nulla. Ho aggiornato la mia risposta con le risposte al tuo commento. Penso che esporre alcuni identificatori sia un buon compromesso tra RESTfulness completa e usabilità.
Mike Partridge,

La mia preoccupazione principale qui è un po 'più pratica. Uso ASP.NET MVC per implementare l'app Web e, a causa di alcune regole interne, devo definire i pattern URL supportati dall'app. Vale a dire se / a / {id} è definito, quindi l'app gestirà / a / 1, ma non / a / 1 / b / 2. Ciò comporta la necessità di ricompilare l'app Web se gli URL dell'API REST cambiano non solo per preservare gli URL con segnalibro, ma anche per far funzionare l'app Web quando si naviga dalla radice. Semplicemente perché i collegamenti ipertestuali incorporati nelle pagine HTML non funzioneranno senza quello.
Pavel Gatilov

2

Ammettiamolo, non esiste una soluzione magica. Hai letto Richardson Maturity Model ? Divide la maturità dell'architettura REST in 3 livelli: risorse, verbi HTTP e controlli hypermedia.

Non dovrei esporre identificatori non elaborati delle mie entità, ma piuttosto restituire hyperlink con rel = "self"

Si tratta di controlli ipermediali. Ne hai davvero bisogno? Questo approccio ha dei benefici molto buoni (puoi leggere qui ). Ma non esistono pasti gratuiti e dovrai lavorare sodo (ad es. La tua seconda soluzione) se vuoi prenderli.

È una questione di equilibrio: vuoi sacrificare le prestazioni (e rendere il tuo codice più complicato) ma ottenere un sistema più flessibile? Oppure preferisci mantenere le cose più veloci e più semplici, ma paga più tardi quando introduci modifiche al tuo api / modello?

Come persona che ha sviluppato un sistema simile (livello di logica aziendale, livello Web e client Web) ho scelto la seconda opzione. Dato che il mio gruppo ha sviluppato tutti i livelli, abbiamo deciso che è meglio avere un po 'di accoppiamento (facendo conoscere al livello Web gli ID entità e costruendo gli API) e in cambio ottenere il codice che è più semplice. Anche la compatibilità con le versioni precedenti non era rilevante nel nostro caso.

Se l'applicazione Web è stata sviluppata da una terza parte o se la compatibilità con le versioni precedenti è stata un problema, avremmo potuto scegliere diversamente perché in questo caso sarebbe stato molto utile poter modificare la struttura dell'URL senza modificare l'applicazione Web. Abbastanza per giustificare la complicazione del codice.

Significa tutto che le API RESTful non possono fungere da backend per le applicazioni Web?

Penso che significhi che non è necessario creare un'implementazione REST perfetta. Puoi andare con la tua seconda soluzione, o esporre l'ID dell'entità o forse passare api url . Va bene finché capisci le implicazioni e i compromessi.


0

Immagino che se ti attacchi a qualcosa di simile al Atom Syndication Formattuo, sei bravo.

Qui i metadati che descrivono la voce rappresentata possono essere specificati utilizzando elementi / attributi aggiuntivi:

  • Secondo [RFC4287] , contiene un URI che identifica in modo univoco la voce

  • Secondo [RFC4287] , questo elemento è facoltativo. Se incluso, contiene l'URI che un client deve utilizzare per recuperare la voce.

Questi sono solo i miei due centesimi.


Forse non ottengo qualcosa, ma mi sembra che la tua risposta non spieghi come generare gli URL di un'app Web che è un client di un'API REST, vero?
Pavel Gatilov,

0

Non preoccuparti degli URL, preoccupati dei tipi di media.

Vedi qui (terzo punto elenco in particolare).

Un'API REST dovrebbe spendere quasi tutto il suo sforzo descrittivo per definire i tipi di media utilizzati per rappresentare le risorse e guidare lo stato dell'applicazione, o per definire nomi di relazioni estese e / o mark-up abilitato per ipertesto per tipi di media standard esistenti. .


Nel caso di una tipica app Web, il client è un essere umano ; il browser è solo un agente .

Quindi un tag anchor come

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

corrisponde a qualcosa di simile

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

L'URL è ancora opaco per l'utente, tutto ciò che le interessa sono i tipi di media (ad es text/html, application/pdf, application/flv, video/x-flv, image/jpeg, image/funny-cat-picture etc.). Il testo descrittivo contenuto nell'ancora (e nell'attributo title) è solo un modo per estendere il tipo di relazione in modo comprensibile agli umani.

Il motivo per cui si desidera che l'URI sia opaco per i client è quello di ridurre l'accoppiamento (uno degli obiettivi principali di REST). Il server può modificare / riorganizzare gli URI senza influire sul client (purché si disponga di una buona politica di memorizzazione nella cache, il che potrebbe non significare affatto la memorizzazione nella cache).

In sintesi

Assicurati solo che il cliente (umano o macchina) si preoccupi dei tipi di media e delle relazioni piuttosto che degli URL e che tu stia bene.


Rodrick, la mia domanda non riguarda la creazione dell'API, ma piuttosto la creazione di un'applicazione Web che si trova in cima a un'API RESTful. Riesco a malapena a capire come i tipi di media possano aiutarmi a creare URL per l'app Web. Sebbene i tipi di supporto siano fondamentali per il contratto di assistenza e la reperibilità.
Pavel Gatilov,

@PavelGatilov - Il client della tua app Web è un essere umano?
Rodrick Chapman,

Sì. E molto poco qualificato.
Pavel Gatilov

0

Il modo più semplice consiste probabilmente nell'utilizzare gli URL API delle risorse come identificatori di stringa. Ma la pagina web ti piace http://my.web.app/person/http%3A%2F%2Fmy.rest.api%2Fapi%2Fperson%2F1234 sono brutti.

Penso che tu abbia ragione, questo è il modo più semplice. Puoi relativizzare gli URL controhttp://my.rest.api/api per renderli meno brutti:

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

Se l'URL fornito dall'API non è relativo a quella base, si degrada nella forma brutta:

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

Per fare un ulteriore passo, ispeziona la risposta dal server API per determinare che tipo di vista vuoi presentare e smetti di codificare il delimitatore e i due punti del percorso:

http://my.web.app/person/1234 (best case)
http://my.web.app/http://other.api.host/api/person/1234 (ugly case)
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.