Modellazione di risorse con collegamenti

2

Contesto

Per essere pienamente conforme ai principi REST enumerati nella tesi di Fielding, una risorsa deve contenere collegamenti che reindirizzano ad altre risorse che hanno una relazione con esso funzionalmente, come per un conto bancario che visualizza dinamicamente un link di prelievo, o semanticamente potrebbe essere correlato a un altro concetto correlato alla risorsa.

Più in generale questo concetto di collegamento è presentato ha HATEOAS. L. Richardson lo ha descritto molto bene come parte del suo Modello di maturità (in particolare il livello 3)

Problema

Da un punto di vista tecnico, i collegamenti non sono così facili da implementare. Sto affrontando in particolare un problema durante la modellazione delle mie risorse in classi. Devo associare diversi campi che corrispondono a un URI. Ecco un esempio di ciò che sembra (basato su questo esempio ), con una risorsa BankingAccount che descrive la sua caratteristica:

class BankingAccount() {
    Double totalAmount;
    String ownerName;
    URI uriToCloseAccount;
    URI uriToWithdrawMoney;
}

Qui, il campo uriToCloseAccount è un URI (= un collegamento) che reindirizza a un'altra risorsa per chiudere l'account. Nella stessa mente, un altro URI uriToWithdrawMoney reindirizzerà a una risorsa che elabora il prelievo.

Potrebbe essere possibile per un client recuperare questa risorsa tramite GET (su URI / bankingAccount / [id]) ma aggiornare anche solo il nome del proprietario, per qualsiasi motivo.

Problema

È realistico supporre che un client possa aggiornare il campo uriToCloseAccount della risorsa di un BankingAccount? Non sto completamente bene con questo. Ho sempre dato per scontato in passato che non è un cliente responsabile della gestione dell'URI. Sono qui solo per indicare un percorso da seguire (o le operazioni da fare, a seconda del formato hypermedia scelto) dal server stesso al client.

Quindi la domanda di fondo è: i link fanno parte del modello di risorsa? Oppure è un'informazione che è al di fuori del modello (non so, forse nel formato scelto?)

Grazie in anticipo!

    
posta AilurusFulgens 22.10.2015 - 14:22
fonte

1 risposta

2

Nel caso qui sotto, no, non dovrebbe essere aggiornato (quindi è di nuovo un uri chiudere un conto restful? o prelevare denaro?) class BankingAccount() { Double totalAmount; String ownerName; URI uriToCloseAccount; URI uriToWithdrawMoney; }

Supponiamo che questo sia il tuo modello:

{
   "links":{
      "self": "/accounts/ACCOUNT_NUM"
      "owner":"/customers/OWNER",
      "transactions": "/transactions?account=ACCOUNT_NUM"
   }
   "number": "ACCOUNT_NUM",
   "balance": "10000 EUR"
}

cambiare il proprietario (supponiamo che le banche lo consentano) sarebbe qualcosa come:

PATCH /accounts/ACCOUNT_NUM
{
   "links":{
      "owner":"/customers/NEW_OWNER",
   }
}

Questo ovviamente richiede l'esistenza di /customers/NEW_OWNER (il client dovrebbe aver controllato prima di inviare l'aggiornamento.

Ora per le transazioni ovviamente non ha senso aggiornare quel link. Quindi l'API non dovrebbe permetterlo.

Se un link è aggiornabile o meno dipende dal significato del link.

Il tuo modello di applicazione interno sarebbe ovviamente diverso e non deve includere collegamenti

class BankingAccount() {
  int accountNumber
  Double totalAmount;
  String ownerId;
}

Ma dovresti aggiungere qualche mappatura extra mentre esegui il marshalling / unmarshalling su json, xml o qualunque sia il tuo formato:

  • accountNumber viene trasformato in links.self /accounts/accountNumberValue
  • ownerId viene trasformato in links.owner /customers/ownerIdValue
  • aggiungi link.transactions in modo che il cliente possa navigare alle transazioni dell'account corrente = > %codice%

chiusura di un account

Come per chiudere un account (che non è una risorsa ma un'operazione su una risorsa) che farebbe parte dei documenti API, ad esempio: /transactions?account=accountNumberValue o:

PATCH /accounts/MY_ACCOUNT
{
   "status":"closed"
}

Ma allora dovresti aggiungere uno stato di campo al tuo modello di account.

Prelievo di denaro

Questo potrebbe essere modellato come un'operazione sulle transazioni.

POST /transactions
{
   "links":{
      "account":"/accounts/MY_ACCOUNT"
   },
   "type":"withdraw",
   "amount": "10000 USD"
}

Si noti che l'account di destinazione è identificato da un collegamento.

Analogia

So che a qualcuno non piace fare l'analogia con i database ma a volte ha senso vedere le risorse e le raccolte di risorse come righe e tabelle:

DATABASE      | API
table         | resource collection
row           | resource
primary key   | self link
foreign key   | link to related resource 
              |   (identified by resource type name for example)
1,n relation  | link to related collection with filter query

SQL

UPDATE accounts SET owner=OWNER_ID WHERE account=ACCOUNT_ID
INSERT INTO transactions (account,type,amount) VALUE (ACCOUNT_ID,"withdrawal","10000 USD")
SELECT * FROM transactions WHERE account=ACCOUNT_ID

API

PATCH /accounts/ACCOUNT_ID
{
   "links": {
     "owner":"/owners/OWNER_ID"
   }
}

POST /transactions
{
   "links":{
      "account":"/accounts/MY_ACCOUNT"
   },
   "type":"withdraw",
   "amount": "10000 USD"
}

GET /transactions?account=MY_ACCOUNT

Perché utilizzare i link per le relazioni anziché gli ID semplici?

Perché non hai campo DELETE /accounts/MY_ACCOUNT sulla risorsa dell'account anziché ownerId ?

Avere campi di id invece di link significa che il client sa come costruire un url per la risorsa che è meno riposante (lo sai ... HATEOAS). Ma è la tua scelta su quanto deve essere riposante l'API.

    
risposta data 26.10.2015 - 17:43
fonte

Leggi altre domande sui tag